Merge branch 'main' of https://github.com/r3econ/mobile into ios-lichess-broadcasts-widget

This commit is contained in:
Rafal Sroka
2026-04-16 22:59:14 +02:00
145 changed files with 2196 additions and 295 deletions
+66 -3
View File
@@ -63,6 +63,41 @@ flutter test test/model/engine/engine_test.dart
flutter test test/model/engine/engine_test.dart --name "test name"
```
### Testing Strategy: Prefer HTTP Mocking Over Provider Overrides
**Always mock at the HTTP layer** (override `httpClientFactoryProvider`) rather than overriding Riverpod providers directly. Reasons:
1. **`autoDispose` + `ref.read` interaction**: Many providers are `FutureProvider.autoDispose` and use `ref.withClientCacheFor` which calls `keepAlive()` to prevent premature disposal. Overriding the provider directly bypasses `keepAlive()`, so the provider can be disposed before its future resolves — causing silent test failures where navigation never happens.
2. **Tests provider logic, not mocks**: Most providers contain real logic (caching, fallback, data transformation) that should be exercised in tests. Replacing a provider with `(_) async => fakeValue` skips all that logic.
**Pattern to use:**
```dart
overrides: {
httpClientFactoryProvider: httpClientFactoryProvider.overrideWith((ref) {
return FakeHttpClientFactory(
() => MockClient((request) async {
if (request.url.path == '/api/puzzle/daily') {
return http.Response(mockDailyPuzzleResponse, 200);
}
return http.Response('', 404);
}),
);
}),
},
```
Direct provider overrides are acceptable for **non-network providers** (repositories backed by mocks, services with no HTTP, etc.) where the provider has no `keepAlive` dependency and the override doesn't skip meaningful logic.
### Analysis Rules (CRITICAL)
**Always run `flutter analyze` on every file you edit, including test files, before finishing.**
Two rules the analyzer enforces that are easy to miss:
- **`const` constructors**: use `const` (not `final`) when constructing a const-capable class. The analyzer will flag `prefer_const_constructors`. This applies everywhere, including test files.
- **No leading underscores for local identifiers**: local variables and functions must not start with `_`. Reserve `_` for library-private top-level or class members.
### Code Quality Checks
```bash
# Static analysis
@@ -100,12 +135,13 @@ The formatter is configured via `analysis_options.yaml` (`formatter: page_width:
### Adding New Translations
1. Edit `translation/source/mobile.xml` for mobile-specific strings
2. Generate ARB files and Dart code:
2. Regenerate everything:
```bash
./scripts/gen-arb.mjs
flutter gen-l10n
./scripts/gen-translations.sh
```
This runs `gen-arb.mjs`, `flutter gen-l10n`, and `gen-widget-strings.mjs` in order.
Mobile-specific translations get a `mobile` prefix (e.g., "foo" becomes `mobileFoo` in Dart).
## Architecture
@@ -353,6 +389,33 @@ Generated files are NOT committed to git.
- **Brand names**: Don't translate names like "Puzzle Storm" or "Puzzle Streak"
- **FVM users**: Remember to prefix commands with `fvm` (e.g., `fvm flutter test`)
## iOS Home Screen Widgets (WidgetKit Extension)
The app includes a native iOS WidgetKit extension (`ios/LichessWidgets/`) providing home screen widgets. See `ios/EXTENSIONS.md` for contributor setup instructions (requires Apple Developer account configuration).
### Architecture
- **`LichessWidgetsBundle.swift`** — `@main` entry point registering all 4 widgets.
- **`LichessAppGroup.swift`** — Reads shared `UserDefaults` from App Group `group.org.lichess.mobileV2.LichessWidgets`:
- `lichessHost`, `boardTheme`, `pieceSet`, `isKidMode`
- **`Deeplinks.swift`** — Custom URI scheme encoding for opening URLs in the in-app browser.
- **Dependencies**: WidgetKit, ChessgroundAssets (Swift Package, shared with Dart), FeedKit, XMLKit.
### Flutter Integration
The Flutter app (via `home_widget` package) writes to the shared App Group from `app.dart`:
- `lichessHost` — server URL
- `boardTheme` / `pieceSet` — board appearance (triggers `DailyPuzzleWidget` reload)
- `isKidMode` — hides blog widgets when active (triggers blog widget reload)
When modifying widget-related settings or board theme/piece set preferences in Dart, ensure the corresponding `HomeWidget.saveWidgetData` call and `HomeWidget.updateWidget` are kept in sync in `lib/src/app.dart`.
Widget UI strings are translated via `ios/LichessWidgets/Localizable.xcstrings` (a String Catalog). To add a new translatable string, register it under `WIDGET_KEYS` in `scripts/gen-widget-strings.mjs` and run `./scripts/gen-translations.sh`.
### Code Signing
The extension has its own bundle ID (`org.lichess.mobileV2.LichessWidgets`) and App Group entitlement. fastlane `sync_code_signing` handles provisioning for both targets (see `ios/fastlane/Matchfile`).
## Debugging
```bash
+5
View File
@@ -8,6 +8,10 @@ Contributions to this project are welcome!
If you want to contribute, please read the [contributing guide](./CONTRIBUTING.md).
If you are new to this project, you can [read the documentation](./docs) to get
started. The [CLAUDE.md](./CLAUDE.md) is also a good resource to understand the
codebase.
## Setup
tl;dr: Install Flutter, clone the repo, run in order:
@@ -53,3 +57,4 @@ Only for members of lichess team.
1. Bump the pubspec.yaml version number. This can be in a PR making a change or a separate PR. Use semantic versioning to determine which part to increment. The version number after the + should also be incremented. For example 0.3.3+000303 with a patch should become 0.3.4+000304.
2. Run workflow [Deploy to Play Store](https://github.com/lichess-org/mobile/actions/workflows/deploy_play_store.yml)
3. [Publish on F-Droid](./docs/publish_fdroid.md)
+7 -5
View File
@@ -14,11 +14,14 @@ ARB files are generated with a script that processes these translations: `script
Then a flutter command is used to generate the dart files from the ARB files.
So, in order to update the dart files we need to run:
A third script, `scripts/gen-widget-strings.mjs`, generates `ios/LichessWidgets/Localizable.xcstrings`
— a String Catalog used by the native iOS widget extension to translate its UI strings. See
[ios/EXTENSIONS.md](../ios/EXTENSIONS.md#internationalisation) for details on adding new widget strings.
All three steps are combined in a single script:
```bash
./scripts/gen-arb.mjs
flutter gen-l10n
./scripts/gen-translations.sh
```
## How to add new translations
@@ -42,8 +45,7 @@ Note that a module can contain a lot of translations that we don't need in the a
Once you've added the module to the script, you can run the script to update the translations.
```bash
./scripts/gen-arb.mjs
flutter gen-l10n
./scripts/gen-translations.sh
```
You should see the new strings in the `lib/l10n/app_*.arb` and `lib/l10n/app_*.dart` files.
+20
View File
@@ -0,0 +1,20 @@
# Publishing a new version to F-Droid
There is a separate `fdroid` branch which uses unified push instead of firebase so that the app can be published on
F-Droid.
To publish, first merge the new version tag into that branch.
Then, ensure that the `flutter-version` file contains the exact version that the new Play Store release was built
against (this is required for f-droid builds being reproducible).
Finally, tag the release and push, this will automatically trigger a new version to be built by the F-Droid server.
```bash
git checkout fdroid
git merge v<new-version>
# If neccessary, update the flutter-version file
# Any tag that ends with ".fdroid" will be picked up by the f-droid server as a new release
git push
git tag v<new-version>.fdroid
git push --tags
```
+20
View File
@@ -51,6 +51,26 @@ This will generate the profile, push it to the certificates repo, and set the co
Also add the new bundle ID to both `app_identifier` arrays in `fastlane/Matchfile` and the `sync_code_signing` call in `fastlane/Fastfile`.
## Internationalisation
Widget UI strings are translated using a String Catalog at `ios/LichessWidgets/Localizable.xcstrings`. This file is generated from the app's ARB translation files and must not be edited by hand.
### How it works
`scripts/gen-widget-strings.mjs` reads every `lib/l10n/app_*.arb` file and extracts the keys listed in its `WIDGET_KEYS` map, then writes them into `Localizable.xcstrings` with all available translations. The String Catalog is picked up automatically by Xcode via the `fileSystemSynchronizedRootGroup` — no project file changes needed.
SwiftUI `Text("Daily Puzzle")` resolves the string at runtime against the catalog using the device locale, so no code changes are needed on the Swift side for existing strings.
### Adding a new translatable string
1. Make sure the string exists in the ARB files (either from Crowdin or from `translation/source/mobile.xml` — see [docs/internationalisation.md](../docs/internationalisation.md)).
2. Add an entry to `WIDGET_KEYS` in `scripts/gen-widget-strings.mjs`:
```js
'English UI string': { arbKey: 'theArbKey', fallback: 'English UI string' },
```
3. Run `./scripts/gen-translations.sh` to regenerate the catalog.
4. Use the exact same English string as a `LocalizedStringKey` in SwiftUI (`Text("English UI string")`).
## Chessboard assets
Board textures, piece images, and theme colour data used by the widgets are
@@ -5,14 +5,6 @@ enum BlogFeedChoice: String {
case communityBlog
case userBlog
var displayName: String {
switch self {
case .officialBlog: return "Official Blog"
case .communityBlog: return "Community Blog"
case .userBlog: return "User Blog"
}
}
func feedURL(username: String?) -> String? {
switch self {
case .officialBlog: return "https://lichess.org/@/Lichess/blog.atom"
@@ -12,11 +12,5 @@ struct BlogFeedEntry: TimelineEntry {
BlogFeedEntry(date: .now, feed: feed, username: username, items: [], error: nil, isKidMode: true)
}
/// Display name for the widget header.
var headerTitle: String {
if feed == .userBlog, let username, !username.isEmpty {
return "@\(username)"
}
return feed.displayName
}
}
@@ -69,7 +69,8 @@ struct BlogFeedWidgetEntryView: View {
var body: some View {
VStack(alignment: .leading, spacing: 0) {
BlogFeedWidgetHeader(feedName: entry.headerTitle,
BlogFeedWidgetHeader(feed: entry.feed,
username: entry.username,
updatedAt: entry.date,
showTimestamp: family != .systemSmall)
Divider()
@@ -90,7 +91,7 @@ struct BlogFeedWidgetEntryView: View {
itemsContent(spec: nil)
.frame(maxWidth: .infinity, alignment: .leading)
Spacer()
Text("Updated at \(entry.date.shortTime)")
Text(entry.date.shortTime)
.font(.system(size: BlogFeedWidgetLayout.secondaryFontSize))
.foregroundStyle(.secondary)
.padding(.top, BlogFeedWidgetLayout.smallFooterTopPadding)
@@ -1,7 +1,8 @@
import SwiftUI
struct BlogFeedWidgetHeader: View {
let feedName: String
let feed: BlogFeedChoice
let username: String?
let updatedAt: Date
var showTimestamp: Bool = true
@@ -11,17 +12,31 @@ struct BlogFeedWidgetHeader: View {
.resizable()
.frame(width: BlogFeedWidgetLayout.logoSize, height: BlogFeedWidgetLayout.logoSize)
HStack(alignment: .lastTextBaseline, spacing: 0) {
Text(feedName)
headerTitle
.font(.system(size: BlogFeedWidgetLayout.titleFontSize, weight: .semibold))
.foregroundStyle(.primary)
.lineLimit(1)
if showTimestamp {
Spacer()
Text("Updated at \(updatedAt.shortTime)")
Text(updatedAt.shortTime)
.font(.system(size: BlogFeedWidgetLayout.secondaryFontSize))
.foregroundStyle(.secondary)
}
}
}
}
@ViewBuilder
private var headerTitle: some View {
switch feed {
case .communityBlog:
Text("Community")
case .officialBlog:
// Resolves via "xBlog %@" key e.g. "Lichess's Blog" / "Blogue de Lichess"
Text("xBlog \(String("Lichess"))")
case .userBlog:
// Resolves via "xBlog %@" key e.g. "johndoe's Blog" / "Blogue de johndoe"
Text("xBlog \(username ?? "")")
}
}
}
@@ -6,8 +6,6 @@ struct DailyPuzzleEntry: TimelineEntry {
let puzzleId: String?
let fen: String?
let lastMove: String?
let rating: Int?
let showRating: Bool
let boardStyle: ChessboardTheme
let error: String?
@@ -18,13 +16,14 @@ struct DailyPuzzleEntry: TimelineEntry {
return parts[1] == "w"
}
var puzzleURL: URL? {
if let id = puzzleId {
return LichessAppGroup.lichessURL(path: "/training/\(id)")
} else {
return LichessAppGroup.lichessURL(path: "/training/daily")
}
}
/// Custom-scheme deeplink handled by the Flutter app to open the native
/// daily-puzzle screen (titled "Daily Puzzle"). See `AppLinksService`.
///
/// Includes the specific puzzle id when known so the app opens the same
/// puzzle the widget is showing (the widget caches the daily puzzle for up
/// to 6 hours, so a plain `/training/daily` deeplink could otherwise open
/// a different puzzle than the one tapped).
var puzzleURL: URL? { dailyPuzzleDeeplink(puzzleId: puzzleId) }
/// A recognisable position (Italian game after initial moves)
/// used as placeholder while the real puzzle loads.
@@ -34,8 +33,6 @@ struct DailyPuzzleEntry: TimelineEntry {
puzzleId: nil,
fen: "1n3rk1/4ppbp/rq1p2p1/3P4/2p1P3/2N2P1n/PPN3PP/R1BQ1R1K b - - 1 1",
lastMove: "g1h1",
rating: 1430,
showRating: true,
boardStyle: ChessboardTheme.fromAppGroup(),
error: nil
)
@@ -13,18 +13,18 @@ struct DailyPuzzleFetcher {
: entry.date.addingTimeInterval(3600)
}
static func fetchEntry(showRating: Bool) async -> DailyPuzzleEntry {
static func fetchEntry() async -> DailyPuzzleEntry {
let boardStyle = ChessboardTheme.fromAppGroup()
guard let url = LichessAppGroup.lichessURL(path: "/api/puzzle/daily") else {
return errorEntry(showRating: showRating, boardStyle: boardStyle)
return errorEntry(boardStyle: boardStyle)
}
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData)
request.setValue("application/json", forHTTPHeaderField: "Accept")
do {
let (data, _) = try await URLSession.shared.data(for: request)
return try parse(data, showRating: showRating, boardStyle: boardStyle)
return try parse(data, boardStyle: boardStyle)
} catch {
return errorEntry(showRating: showRating, boardStyle: boardStyle)
return errorEntry(boardStyle: boardStyle)
}
}
@@ -33,34 +33,27 @@ struct DailyPuzzleFetcher {
private struct APIResponse: Decodable {
struct Puzzle: Decodable {
let id: String
let rating: Int
let fen: String
let lastMove: String
}
let puzzle: Puzzle
}
private static func parse(_ data: Data,
showRating: Bool,
boardStyle: ChessboardTheme) throws -> DailyPuzzleEntry {
private static func parse(_ data: Data, boardStyle: ChessboardTheme) throws -> DailyPuzzleEntry {
let response = try JSONDecoder().decode(APIResponse.self, from: data)
return DailyPuzzleEntry(date: .now,
puzzleId: response.puzzle.id,
fen: response.puzzle.fen,
lastMove: response.puzzle.lastMove,
rating: response.puzzle.rating,
showRating: showRating,
boardStyle: boardStyle,
error: nil)
}
private static func errorEntry(showRating: Bool, boardStyle: ChessboardTheme) -> DailyPuzzleEntry {
private static func errorEntry(boardStyle: ChessboardTheme) -> DailyPuzzleEntry {
DailyPuzzleEntry(date: .now,
puzzleId: nil,
fen: nil,
lastMove: nil,
rating: nil,
showRating: showRating,
boardStyle: boardStyle,
error: "Could not load puzzle")
}
@@ -1,32 +1,21 @@
import AppIntents
import WidgetKit
struct DailyPuzzleIntent: WidgetConfigurationIntent {
static var title: LocalizedStringResource = "Daily Puzzle"
static var description = IntentDescription("Configure the Daily Puzzle widget.")
@Parameter(title: "Show Rating", default: true)
var showRating: Bool
static var parameterSummary: some ParameterSummary {
Summary("Show rating: \(\.$showRating)")
}
}
struct DailyPuzzleProvider: AppIntentTimelineProvider {
struct DailyPuzzleProvider: TimelineProvider {
func placeholder(in context: Context) -> DailyPuzzleEntry {
.placeholder
}
func snapshot(for configuration: DailyPuzzleIntent, in context: Context) async -> DailyPuzzleEntry {
func getSnapshot(in context: Context, completion: @escaping (DailyPuzzleEntry) -> Void) {
// Always return the placeholder snapshot is called for the widget gallery
// and edit sheet, where a fast static preview is more appropriate than a
// live network fetch.
.placeholder
completion(.placeholder)
}
func timeline(for configuration: DailyPuzzleIntent, in context: Context) async -> Timeline<DailyPuzzleEntry> {
let entry = await DailyPuzzleFetcher.fetchEntry(showRating: configuration.showRating)
return Timeline(entries: [entry], policy: .after(DailyPuzzleFetcher.nextUpdate(for: entry)))
func getTimeline(in context: Context, completion: @escaping (Timeline<DailyPuzzleEntry>) -> Void) {
Task {
let entry = await DailyPuzzleFetcher.fetchEntry()
completion(Timeline(entries: [entry], policy: .after(DailyPuzzleFetcher.nextUpdate(for: entry))))
}
}
}
@@ -1,4 +1,3 @@
import AppIntents
import SwiftUI
import WidgetKit
@@ -9,11 +8,7 @@ struct DailyPuzzleWidget: Widget {
let kind = "DailyPuzzleLargeWidget"
var body: some WidgetConfiguration {
AppIntentConfiguration(
kind: kind,
intent: DailyPuzzleIntent.self,
provider: DailyPuzzleProvider()
) { entry in
StaticConfiguration(kind: kind, provider: DailyPuzzleProvider()) { entry in
DailyPuzzleWidgetView(entry: entry)
.containerBackground(.background, for: .widget)
}
@@ -11,15 +11,10 @@ enum DailyPuzzleWidgetLayout {
static let headerBottomPadding: CGFloat = 5
static let logoSize: CGFloat = 20
// Side-to-move indicator
static let sideIndicatorSize: CGFloat = 14
static let sideIndicatorBorderOpacity: CGFloat = 0.4
static let sideIndicatorBorderWidth: CGFloat = 0.5
// Board
static let boardBorderWidth: CGFloat = 1
static let horizontalPadding: CGFloat = 16
static let topPadding: CGFloat = 11
static let verticalPadding: CGFloat = 10
// Piece
static let pieceSizeFactor: CGFloat = 0.9
@@ -18,61 +18,36 @@ struct DailyPuzzleWidgetView: View {
@ViewBuilder
private var contentView: some View {
GeometryReader { geo in
VStack(spacing: 0) {
HStack(spacing: DailyPuzzleWidgetLayout.headerSpacing) {
Image("LichessLogo")
.resizable()
.frame(
width: DailyPuzzleWidgetLayout.logoSize,
height: DailyPuzzleWidgetLayout.logoSize
)
Text("Daily Puzzle")
.font(.system(size: DailyPuzzleWidgetLayout.titleFontSize, weight: .semibold))
.foregroundStyle(.primary)
.lineLimit(1)
Spacer()
if entry.showRating, let rating = entry.rating {
Image(systemName: "chart.line.uptrend.xyaxis")
.font(.system(size: DailyPuzzleWidgetLayout.metaFontSize))
.foregroundStyle(.secondary)
Text("\(rating)")
.font(.system(size: DailyPuzzleWidgetLayout.metaFontSize))
.foregroundStyle(.secondary)
}
Circle()
.fill(entry.isWhiteToMove ? Color.white : Color.black)
.overlay(
Circle().stroke(
Color.primary.opacity(DailyPuzzleWidgetLayout.sideIndicatorBorderOpacity),
lineWidth: DailyPuzzleWidgetLayout.sideIndicatorBorderWidth
)
)
.frame(
width: DailyPuzzleWidgetLayout.sideIndicatorSize,
height: DailyPuzzleWidgetLayout.sideIndicatorSize
)
Text(entry.date.shortTime)
.font(.system(size: DailyPuzzleWidgetLayout.metaFontSize))
.foregroundStyle(.secondary)
}
.padding(.bottom, DailyPuzzleWidgetLayout.headerBottomPadding)
boardView
.clipShape(ContainerRelativeShape())
.overlay(
ContainerRelativeShape()
.stroke(.tertiary, lineWidth: DailyPuzzleWidgetLayout.boardBorderWidth)
VStack(spacing: 0) {
HStack(spacing: DailyPuzzleWidgetLayout.headerSpacing) {
Image("LichessLogo")
.resizable()
.frame(
width: DailyPuzzleWidgetLayout.logoSize,
height: DailyPuzzleWidgetLayout.logoSize
)
.frame(width: geo.size.width, height: geo.size.width)
Text("Daily Puzzle")
.font(.system(size: DailyPuzzleWidgetLayout.titleFontSize, weight: .semibold))
.foregroundStyle(.primary)
.lineLimit(1)
Spacer()
Text(entry.date, format: entry.date.widgetDateFormat)
.font(.system(size: DailyPuzzleWidgetLayout.metaFontSize))
.foregroundStyle(.secondary)
}
.padding(.bottom, DailyPuzzleWidgetLayout.headerBottomPadding)
boardView
.clipShape(ContainerRelativeShape())
.overlay(
ContainerRelativeShape()
.stroke(.tertiary, lineWidth: DailyPuzzleWidgetLayout.boardBorderWidth)
)
}
.padding(.horizontal, DailyPuzzleWidgetLayout.horizontalPadding)
.padding(.top, DailyPuzzleWidgetLayout.topPadding)
.padding(.vertical, DailyPuzzleWidgetLayout.verticalPadding)
}
@ViewBuilder
@@ -106,3 +81,16 @@ struct DailyPuzzleWidgetView: View {
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
#Preview("Puzzle Brown", as: .systemLarge) {
DailyPuzzleWidget()
} timeline: {
DailyPuzzleEntry(
date: .now,
puzzleId: "abcd1",
fen: "1n3rk1/4ppbp/rq1p2p1/3P4/2p1P3/2N2P1n/PPN3PP/R1BQ1R1K b - - 1 1",
lastMove: "g1h1",
boardStyle: .from(themeName: "brown"),
error: nil
)
}
+15 -3
View File
@@ -1,12 +1,24 @@
import Foundation
// Note: puzzle widgets use plain HTTPS URLs (e.g. lichess.org/training/{id})
// so the app can handle them as universal links and open the native puzzle screen directly.
// Note: the daily-puzzle widget uses the `org.lichess.mobile://training/daily`
// custom scheme (optionally suffixed with `/{puzzleId}`) so the Flutter app
// always opens the native "Daily Puzzle" screen for the tapped puzzle.
private let kScheme = "org.lichess.mobile"
/// Builds `org.lichess.mobile://training/daily[/{puzzleId}]` deeplinks handled
/// by the Flutter app to open the native daily-puzzle screen. See `AppLinksService`.
func dailyPuzzleDeeplink(puzzleId: String?) -> URL? {
if let puzzleId {
return URL(string: "\(kScheme)://training/daily/\(puzzleId)")
}
return URL(string: "\(kScheme)://training/daily")
}
extension String {
/// Encodes the string into the custom scheme the app listens for to open it in the in-app browser.
var lichessWebURL: URL? {
guard let encoded = addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return nil }
return URL(string: "org.lichess.mobile://open-web?url=\(encoded)")
return URL(string: "\(kScheme)://open-web?url=\(encoded)")
}
}
@@ -4,9 +4,11 @@ extension Date {
/// "14:53" used for "Updated at" timestamps.
var shortTime: String { formatted(.dateTime.hour().minute()) }
/// "Mar 19" for the current year, "Mar 19, 2025" for a past/future year.
var widgetDateFormat: FormatStyle {
/// "14 avr." / "Apr 14" abbreviated day+month in the device locale.
/// Includes the year when the date falls outside the current year.
var widgetDateFormat: Date.FormatStyle {
let sameYear = Calendar.current.isDate(self, equalTo: .now, toGranularity: .year)
return sameYear ? .dateTime.month(.abbreviated).day() : .dateTime.month(.abbreviated).day().year()
let base = Date.FormatStyle(locale: .current).month(.abbreviated).day()
return sameYear ? base : base.year()
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
import Foundation
enum LichessAppGroup {
static let id = "group.org.lichess.mobileV2"
static let id = "group.org.lichess.mobileV2.LichessWidgets"
// Keys must match the strings passed to HomeWidget.saveWidgetData in lib/src/app.dart.
static let kidModeKey = "isKidMode"
+825
View File
@@ -0,0 +1,825 @@
{
"sourceLanguage": "en",
"strings": {
"Daily Puzzle": {
"extractionState": "manual",
"localizations": {
"af": {
"stringUnit": {
"state": "translated",
"value": "Daaglikse Raaisel"
}
},
"ar": {
"stringUnit": {
"state": "translated",
"value": "اللغز اليومي"
}
},
"be": {
"stringUnit": {
"state": "translated",
"value": "Штодзённыя задачы"
}
},
"bg": {
"stringUnit": {
"state": "translated",
"value": "Задача на деня"
}
},
"bn": {
"stringUnit": {
"state": "translated",
"value": "দৈনিক ধাঁধা"
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "Problem dana"
}
},
"ca": {
"stringUnit": {
"state": "translated",
"value": "Problema del dia"
}
},
"cs": {
"stringUnit": {
"state": "translated",
"value": "Denní úloha"
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "Daglig taktikopgave"
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Taktikaufgabe des Tages"
}
},
"el": {
"stringUnit": {
"state": "translated",
"value": "Τακτικό της ημέρας"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Daily Puzzle"
}
},
"eo": {
"stringUnit": {
"state": "translated",
"value": "Taga puzlo"
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Ejercicio del día"
}
},
"et": {
"stringUnit": {
"state": "translated",
"value": "Päeva pusle"
}
},
"eu": {
"stringUnit": {
"state": "translated",
"value": "Eguneroko ariketa"
}
},
"fa": {
"stringUnit": {
"state": "translated",
"value": "معمای روزانه"
}
},
"fi": {
"stringUnit": {
"state": "translated",
"value": "Päivän tehtävä"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Problème du jour"
}
},
"gl": {
"stringUnit": {
"state": "translated",
"value": "Crebacabezas do día"
}
},
"gsw": {
"stringUnit": {
"state": "translated",
"value": "Täglichi Ufgab"
}
},
"he": {
"stringUnit": {
"state": "translated",
"value": "החידה היומית"
}
},
"hi": {
"stringUnit": {
"state": "translated",
"value": "दैनिक पहेली"
}
},
"hr": {
"stringUnit": {
"state": "translated",
"value": "Dnevna zagonetka"
}
},
"hu": {
"stringUnit": {
"state": "translated",
"value": "Napi feladvány"
}
},
"hy": {
"stringUnit": {
"state": "translated",
"value": "Ամենօրյա Խնդիր"
}
},
"id": {
"stringUnit": {
"state": "translated",
"value": "Teka-teki harian"
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "Tattica di oggi"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "本日の問題"
}
},
"kk": {
"stringUnit": {
"state": "translated",
"value": "Күнделікті жұмбақ"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "일일 퍼즐"
}
},
"lt": {
"stringUnit": {
"state": "translated",
"value": "Dienos Galvosūkis"
}
},
"lv": {
"stringUnit": {
"state": "translated",
"value": "Šodienas uzdevums"
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "Dagens nøtt"
}
},
"nl": {
"stringUnit": {
"state": "translated",
"value": "Dagelijkse Puzzel"
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "Zadanie dnia"
}
},
"pt": {
"stringUnit": {
"state": "translated",
"value": "Problema diário"
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "Problema diário"
}
},
"ro": {
"stringUnit": {
"state": "translated",
"value": "Puzzle Zilnic"
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "Задача дня"
}
},
"sk": {
"stringUnit": {
"state": "translated",
"value": "Úloha na tento deň"
}
},
"sl": {
"stringUnit": {
"state": "translated",
"value": "Uganka dneva"
}
},
"sv": {
"stringUnit": {
"state": "translated",
"value": "Dagens problem"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "Günlük Bulmaca"
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "Щоденна задача"
}
},
"uz": {
"stringUnit": {
"state": "translated",
"value": "Kun masalasi"
}
},
"vi": {
"stringUnit": {
"state": "translated",
"value": "Câu đố Hàng ngày"
}
},
"zh": {
"stringUnit": {
"state": "translated",
"value": "每日棋谜"
}
},
"zh-TW": {
"stringUnit": {
"state": "translated",
"value": "每日謎題"
}
}
}
},
"Community": {
"extractionState": "manual",
"localizations": {
"ar": {
"stringUnit": {
"state": "translated",
"value": "المجتمع"
}
},
"bg": {
"stringUnit": {
"state": "translated",
"value": "Общност"
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "Zajednica"
}
},
"ca": {
"stringUnit": {
"state": "translated",
"value": "Comunitat"
}
},
"cs": {
"stringUnit": {
"state": "translated",
"value": "Komunita"
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "Fællesskab"
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Gemeinschaft"
}
},
"el": {
"stringUnit": {
"state": "translated",
"value": "Κοινότητα"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Community"
}
},
"eo": {
"stringUnit": {
"state": "translated",
"value": "Komunumo"
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Comunidad"
}
},
"eu": {
"stringUnit": {
"state": "translated",
"value": "Komunitatea"
}
},
"fa": {
"stringUnit": {
"state": "translated",
"value": "همدارگان"
}
},
"fi": {
"stringUnit": {
"state": "translated",
"value": "Yhteisön blogit"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Communauté"
}
},
"gl": {
"stringUnit": {
"state": "translated",
"value": "Comunidade"
}
},
"gsw": {
"stringUnit": {
"state": "translated",
"value": "G'meinschaft"
}
},
"hu": {
"stringUnit": {
"state": "translated",
"value": "Közösség"
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "Comunità"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "コミュニティ"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "커뮤니티"
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "Fellesskap"
}
},
"nl": {
"stringUnit": {
"state": "translated",
"value": "Gemeenschap"
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "Społeczność"
}
},
"pt": {
"stringUnit": {
"state": "translated",
"value": "Comunidade"
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "Comunidade"
}
},
"ro": {
"stringUnit": {
"state": "translated",
"value": "Comunitate"
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "Сообщество"
}
},
"sk": {
"stringUnit": {
"state": "translated",
"value": "Komunita"
}
},
"sl": {
"stringUnit": {
"state": "translated",
"value": "Skupnost"
}
},
"sq": {
"stringUnit": {
"state": "translated",
"value": "Bashkësi"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "Topluluk"
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "Спільнота"
}
},
"uz": {
"stringUnit": {
"state": "translated",
"value": "Hamjamiyat"
}
},
"vi": {
"stringUnit": {
"state": "translated",
"value": "Cộng đồng"
}
},
"zh": {
"stringUnit": {
"state": "translated",
"value": "社区"
}
},
"zh-TW": {
"stringUnit": {
"state": "translated",
"value": "社群"
}
}
}
},
"xBlog %@": {
"extractionState": "manual",
"localizations": {
"af": {
"stringUnit": {
"state": "translated",
"value": "%@ se webjoernaal"
}
},
"ar": {
"stringUnit": {
"state": "translated",
"value": "مدونة %@"
}
},
"be": {
"stringUnit": {
"state": "translated",
"value": "Блог %@"
}
},
"bg": {
"stringUnit": {
"state": "translated",
"value": "Блогът на %@"
}
},
"bs": {
"stringUnit": {
"state": "translated",
"value": "Blog korisnika %@"
}
},
"ca": {
"stringUnit": {
"state": "translated",
"value": "Blog de %@"
}
},
"cs": {
"stringUnit": {
"state": "translated",
"value": "Blog hráče %@"
}
},
"da": {
"stringUnit": {
"state": "translated",
"value": "Blog - %@"
}
},
"de": {
"stringUnit": {
"state": "translated",
"value": "Blog von %@"
}
},
"el": {
"stringUnit": {
"state": "translated",
"value": "Ιστολόγιο του χρήστη %@"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "%@'s Blog"
}
},
"eo": {
"stringUnit": {
"state": "translated",
"value": "Blogo de %@"
}
},
"es": {
"stringUnit": {
"state": "translated",
"value": "Blog de %@"
}
},
"et": {
"stringUnit": {
"state": "translated",
"value": "Kasutaja %@ blogi"
}
},
"eu": {
"stringUnit": {
"state": "translated",
"value": "%@ erabiltzailearen Bloga"
}
},
"fa": {
"stringUnit": {
"state": "translated",
"value": "وبنوشتِ %@"
}
},
"fi": {
"stringUnit": {
"state": "translated",
"value": "Käyttäjän %@ blogi"
}
},
"fr": {
"stringUnit": {
"state": "translated",
"value": "Blogue de %@"
}
},
"gl": {
"stringUnit": {
"state": "translated",
"value": "O Blog de %@"
}
},
"he": {
"stringUnit": {
"state": "translated",
"value": "הבלוג של %@"
}
},
"hi": {
"stringUnit": {
"state": "translated",
"value": "%@ का ब्लॉग"
}
},
"hr": {
"stringUnit": {
"state": "translated",
"value": "Blog korisnika %@"
}
},
"hu": {
"stringUnit": {
"state": "translated",
"value": "%@ blogja"
}
},
"hy": {
"stringUnit": {
"state": "translated",
"value": "%@-ի բլոգ"
}
},
"id": {
"stringUnit": {
"state": "translated",
"value": "Blog dari %@"
}
},
"it": {
"stringUnit": {
"state": "translated",
"value": "Blog di %@"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "%@ のブログ"
}
},
"kk": {
"stringUnit": {
"state": "translated",
"value": "%@ блогі"
}
},
"ko": {
"stringUnit": {
"state": "translated",
"value": "%@의 블로그"
}
},
"lt": {
"stringUnit": {
"state": "translated",
"value": "%@ Tinklaraštis"
}
},
"lv": {
"stringUnit": {
"state": "translated",
"value": "Lietotāja %@ blogs"
}
},
"nb": {
"stringUnit": {
"state": "translated",
"value": "Bloggen til %@"
}
},
"nl": {
"stringUnit": {
"state": "translated",
"value": "Blog van %@"
}
},
"pl": {
"stringUnit": {
"state": "translated",
"value": "Blog gracza %@"
}
},
"pt": {
"stringUnit": {
"state": "translated",
"value": "Blog de %@"
}
},
"pt-BR": {
"stringUnit": {
"state": "translated",
"value": "Blog do(a) %@"
}
},
"ro": {
"stringUnit": {
"state": "translated",
"value": "Blog-ul lui %@"
}
},
"ru": {
"stringUnit": {
"state": "translated",
"value": "Блог %@"
}
},
"sk": {
"stringUnit": {
"state": "translated",
"value": "Blog užívateľa %@"
}
},
"sl": {
"stringUnit": {
"state": "translated",
"value": "%@ blog"
}
},
"sq": {
"stringUnit": {
"state": "translated",
"value": "Blogu i %@"
}
},
"sv": {
"stringUnit": {
"state": "translated",
"value": "%@s blogg"
}
},
"tr": {
"stringUnit": {
"state": "translated",
"value": "%@ Bloğu"
}
},
"uk": {
"stringUnit": {
"state": "translated",
"value": "Блог %@"
}
},
"uz": {
"stringUnit": {
"state": "translated",
"value": "%@ blogi"
}
},
"vi": {
"stringUnit": {
"state": "translated",
"value": "Bài viết của %@"
}
},
"zh": {
"stringUnit": {
"state": "translated",
"value": "%@ 的博客"
}
},
"zh-TW": {
"stringUnit": {
"state": "translated",
"value": "%@的部落格"
}
}
}
}
},
"version": "1.0"
}
@@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "LaunchImage.png",
"filename" : "LichessLogo.png",
"idiom" : "universal",
"scale" : "1x"
},
@@ -12,12 +12,12 @@
"value" : "dark"
}
],
"filename" : "LaunchImageDark.png",
"filename" : "LichessLogoDark.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "LaunchImage@2x.png",
"filename" : "LichessLogo@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
@@ -28,12 +28,12 @@
"value" : "dark"
}
],
"filename" : "LaunchImageDark@2x.png",
"filename" : "LichessLogoDark@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "LaunchImage@3x.png",
"filename" : "LichessLogo@3x.png",
"idiom" : "universal",
"scale" : "3x"
},
@@ -44,7 +44,7 @@
"value" : "dark"
}
],
"filename" : "LaunchImageDark@3x.png",
"filename" : "LichessLogoDark@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

+44 -44
View File
@@ -44,61 +44,61 @@ PODS:
- file_picker (0.0.1):
- DKImagePickerController/PhotoGallery
- Flutter
- Firebase/CoreOnly (12.9.0):
- FirebaseCore (~> 12.9.0)
- Firebase/Crashlytics (12.9.0):
- Firebase/CoreOnly (12.12.0):
- FirebaseCore (~> 12.12.0)
- Firebase/Crashlytics (12.12.0):
- Firebase/CoreOnly
- FirebaseCrashlytics (~> 12.9.0)
- Firebase/Messaging (12.9.0):
- FirebaseCrashlytics (~> 12.12.0)
- Firebase/Messaging (12.12.0):
- Firebase/CoreOnly
- FirebaseMessaging (~> 12.9.0)
- firebase_core (4.6.0):
- Firebase/CoreOnly (= 12.9.0)
- FirebaseMessaging (~> 12.12.0)
- firebase_core (4.7.0):
- Firebase/CoreOnly (= 12.12.0)
- Flutter
- firebase_crashlytics (5.1.0):
- Firebase/Crashlytics (= 12.9.0)
- firebase_crashlytics (5.2.0):
- Firebase/Crashlytics (= 12.12.0)
- firebase_core
- Flutter
- firebase_messaging (16.1.3):
- Firebase/Messaging (= 12.9.0)
- firebase_messaging (16.2.0):
- Firebase/Messaging (= 12.12.0)
- firebase_core
- Flutter
- FirebaseCore (12.9.0):
- FirebaseCoreInternal (~> 12.9.0)
- FirebaseCore (12.12.0):
- FirebaseCoreInternal (~> 12.12.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Logger (~> 8.1)
- FirebaseCoreExtension (12.9.0):
- FirebaseCore (~> 12.9.0)
- FirebaseCoreInternal (12.9.0):
- FirebaseCoreExtension (12.12.0):
- FirebaseCore (~> 12.12.0)
- FirebaseCoreInternal (12.12.0):
- "GoogleUtilities/NSData+zlib (~> 8.1)"
- FirebaseCrashlytics (12.9.0):
- FirebaseCore (~> 12.9.0)
- FirebaseInstallations (~> 12.9.0)
- FirebaseRemoteConfigInterop (~> 12.9.0)
- FirebaseSessions (~> 12.9.0)
- FirebaseCrashlytics (12.12.0):
- FirebaseCore (~> 12.12.0)
- FirebaseInstallations (~> 12.12.0)
- FirebaseRemoteConfigInterop (~> 12.12.0)
- FirebaseSessions (~> 12.12.0)
- GoogleDataTransport (~> 10.1)
- GoogleUtilities/Environment (~> 8.1)
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
- FirebaseInstallations (12.9.0):
- FirebaseCore (~> 12.9.0)
- FirebaseInstallations (12.12.0):
- FirebaseCore (~> 12.12.0)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- PromisesObjC (~> 2.4)
- FirebaseMessaging (12.9.0):
- FirebaseCore (~> 12.9.0)
- FirebaseInstallations (~> 12.9.0)
- FirebaseMessaging (12.12.0):
- FirebaseCore (~> 12.12.0)
- FirebaseInstallations (~> 12.12.0)
- GoogleDataTransport (~> 10.1)
- GoogleUtilities/AppDelegateSwizzler (~> 8.1)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/Reachability (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
- nanopb (~> 3.30910.0)
- FirebaseRemoteConfigInterop (12.9.0)
- FirebaseSessions (12.9.0):
- FirebaseCore (~> 12.9.0)
- FirebaseCoreExtension (~> 12.9.0)
- FirebaseInstallations (~> 12.9.0)
- FirebaseRemoteConfigInterop (12.12.0)
- FirebaseSessions (12.12.0):
- FirebaseCore (~> 12.12.0)
- FirebaseCoreExtension (~> 12.12.0)
- FirebaseInstallations (~> 12.12.0)
- GoogleDataTransport (~> 10.1)
- GoogleUtilities/Environment (~> 8.1)
- GoogleUtilities/UserDefaults (~> 8.1)
@@ -297,18 +297,18 @@ SPEC CHECKSUMS:
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
Firebase: 065f2bb395062046623036d8e6dc857bc2521d56
firebase_core: 8e6f58412ca227827c366b92e7cee047a2148c60
firebase_crashlytics: c399f9682c474beb06a89c11dfab04db537f3cd2
firebase_messaging: c3aa897e0d40109cfb7927c40dc0dea799863f3b
FirebaseCore: 428912f751178b06bef0a1793effeb4a5e09a9b8
FirebaseCoreExtension: e911052d59cd0da237a45d706fc0f81654f035c1
FirebaseCoreInternal: b321eafae5362113bc182956fafc9922cfc77b72
FirebaseCrashlytics: 43913d587ef07beaf5db703baa61eacf9554658c
FirebaseInstallations: 7b64ffd006032b2b019a59b803858df5112d9eaa
FirebaseMessaging: 7d6cdbff969127c4151c824fe432f0e301210f15
FirebaseRemoteConfigInterop: 765ee19cd2bfa8e54937c8dae901eb634ad6787d
FirebaseSessions: a2d06fd980431fda934c7a543901aca05fc4edcc
Firebase: aa154fee4e9b8eac17aa42344988865b3e857d33
firebase_core: 9156a152117c843440b0b990c785aa0259bc5447
firebase_crashlytics: e24acd48861c5edf6e0f6c134d6a0b28593c76d7
firebase_messaging: 0d962ab44ff24ed36deb8fa2ee043c4671858269
FirebaseCore: f28af0427998cd53f8d4826bce17260e33224053
FirebaseCoreExtension: ff6fd42eb5287e71d3e160450de6509733d9ead7
FirebaseCoreInternal: 7c12fc3011d889085e765e317d7b9fd1cef97af9
FirebaseCrashlytics: 2a38be892de8417a06e929a13fec0971afcb4dfc
FirebaseInstallations: 4e6e162aa4abaaeeeb01dd00179dfc5ad9c2194e
FirebaseMessaging: 341004946fa7ffc741344b20f1b667514fc93e31
FirebaseRemoteConfigInterop: 23996ab7397494722df4fdd1fd398024389d5da8
FirebaseSessions: 804bd321f2d2f2ddafe74ef7856062aa19f179c2
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_local_notifications: 643a3eda1ce1c0599413ca31672536d423dee214
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
+2 -1
View File
@@ -1562,5 +1562,6 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} jaar gelede} other{{count} jare gelede}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{nog {count} minuut oor} other{nog {count} minute oor}}",
"timeagoNbHoursRemaining": "{count, plural, =1{nog {count} uur oor} other{nog {count} ure oor}}",
"tfaTwoFactorAuth": "Tweeledige verifikasie"
"tfaTwoFactorAuth": "Tweeledige verifikasie",
"ublogXBlog": "{param} se webjoernaal"
}
+3 -1
View File
@@ -1720,5 +1720,7 @@
"timeagoNbYearsAgo": "{count, plural, =0{منذ {count} سنة} =1{منذ {count} سنة} =2{منذ {count} سنة} few{منذ {count} سنة} many{منذ {count} سنة} other{منذ {count} سنة}}",
"timeagoNbMinutesRemaining": "{count, plural, =0{{count} دقيقة متبقية} =1{{count} دقيقة متبقية} =2{{count} دقيقة متبقية} few{{count} دقيقة متبقية} many{{count} دقيقة متبقية} other{{count} دقيقة متبقية}}",
"timeagoNbHoursRemaining": "{count, plural, =0{{count} ساعة متبقية} =1{{count} ساعة متبقية} =2{{count} ساعة متبقية} few{{count} ساعة متبقية} many{{count} ساعة متبقية} other{{count} ساعة متبقية}}",
"tfaTwoFactorAuth": "التوثيق ذو العاملين"
"tfaTwoFactorAuth": "التوثيق ذو العاملين",
"ublogCommunity": "المجتمع",
"ublogXBlog": "مدونة {param}"
}
+2 -1
View File
@@ -1611,5 +1611,6 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} год таму} few{{count} гады таму} many{{count} гадоў таму} other{{count} гадоў таму}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{Засталася {count} хвіліна} few{Засталося {count} хвіліны} many{Засталося {count} хвілін} other{Засталося {count} хвіліны}}",
"timeagoNbHoursRemaining": "{count, plural, =1{Засталася {count} гадзіна} few{Засталося {count} гадзіны} many{Засталося {count} гадзін} other{Засталося {count} гадзіны}}",
"tfaTwoFactorAuth": "Двухфактарная аўтэнтыфікацыя"
"tfaTwoFactorAuth": "Двухфактарная аўтэнтыфікацыя",
"ublogXBlog": "Блог {param}"
}
+3 -1
View File
@@ -1774,5 +1774,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{преди {count} година} other{преди {count} години}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{остава {count} минутa} other{остават {count} минути}}",
"timeagoNbHoursRemaining": "{count, plural, =1{остава {count} час} other{остават {count} часа}}",
"tfaTwoFactorAuth": "Двуфакторно удостоверяване"
"tfaTwoFactorAuth": "Двуфакторно удостоверяване",
"ublogCommunity": "Общност",
"ublogXBlog": "Блогът на {param}"
}
+3 -1
View File
@@ -1749,5 +1749,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{prije {count} godinu} few{prije {count} godine} other{prije {count} godina}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{Preostala je {count} minuta} few{Preostalo je {count} minuta} other{Preostalo je {count} minuta}}",
"timeagoNbHoursRemaining": "{count, plural, =1{Preostao je {count} sat} few{Preostalo je {count} sata} other{Preostalo je {count} sati}}",
"tfaTwoFactorAuth": "Dvofaktorska provjera autentičnosti"
"tfaTwoFactorAuth": "Dvofaktorska provjera autentičnosti",
"ublogCommunity": "Zajednica",
"ublogXBlog": "Blog korisnika {param}"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{fa {count} any} other{fa {count} anys}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{Queda {count} minut} other{Queden {count} minuts}}",
"timeagoNbHoursRemaining": "{count, plural, =1{Queda {count} hora} other{Queden {count} hores}}",
"tfaTwoFactorAuth": "Autenticació de dos factors"
"tfaTwoFactorAuth": "Autenticació de dos factors",
"ublogCommunity": "Comunitat",
"ublogXBlog": "Blog de {param}"
}
+3 -1
View File
@@ -1749,5 +1749,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{před {count} rokem} few{před {count} lety} many{před {count} lety} other{před {count} lety}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{Zbývá {count} minuta} few{Zbývají {count} minuty} many{Zbývá {count} minut} other{Zbývá {count} minut}}",
"timeagoNbHoursRemaining": "{count, plural, =1{Zbývá {count} hodina} few{Zbývají {count} hodiny} many{Zbývá {count} hodin} other{Zbývá {count} hodin}}",
"tfaTwoFactorAuth": "Dvoufázové ověření"
"tfaTwoFactorAuth": "Dvoufázové ověření",
"ublogCommunity": "Komunita",
"ublogXBlog": "Blog hráče {param}"
}
+3 -1
View File
@@ -1823,5 +1823,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} år siden} other{{count} år siden}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minut tilbage} other{{count} minutter tilbage}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} time tilbage} other{{count} timer tilbage}}",
"tfaTwoFactorAuth": "To-faktor-godkendelse"
"tfaTwoFactorAuth": "To-faktor-godkendelse",
"ublogCommunity": "Fællesskab",
"ublogXBlog": "Blog - {param}"
}
+3 -1
View File
@@ -1824,5 +1824,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{vor {count} Jahr} other{vor {count} Jahren}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} Minute verbleibend} other{{count} Minuten verbleibend}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} Stunde verbleiben} other{{count} Stunden verbleiben}}",
"tfaTwoFactorAuth": "Zwei-Faktor-Authentifizierung"
"tfaTwoFactorAuth": "Zwei-Faktor-Authentifizierung",
"ublogCommunity": "Gemeinschaft",
"ublogXBlog": "Blog von {param}"
}
+3 -1
View File
@@ -1811,5 +1811,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} χρόνο πριν} other{{count} χρόνια πριν}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{απομένει {count} λεπτό} other{απομένουν {count} λεπτά}}",
"timeagoNbHoursRemaining": "{count, plural, =1{απομένει {count} ώρα} other{απομένουν {count} ώρες}}",
"tfaTwoFactorAuth": "Έλεγχος ταυτότητας δύο παραγόντων"
"tfaTwoFactorAuth": "Έλεγχος ταυτότητας δύο παραγόντων",
"ublogCommunity": "Κοινότητα",
"ublogXBlog": "Ιστολόγιο του χρήστη {param}"
}
+10 -1
View File
@@ -3732,5 +3732,14 @@
}
}
},
"tfaTwoFactorAuth": "Two-factor authentication"
"tfaTwoFactorAuth": "Two-factor authentication",
"ublogCommunity": "Community",
"ublogXBlog": "{param}'s Blog",
"@ublogXBlog": {
"placeholders": {
"param": {
"type": "String"
}
}
}
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} year ago} other{{count} years ago}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minute remaining} other{{count} minutes remaining}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} hour remaining} other{{count} hours remaining}}",
"tfaTwoFactorAuth": "Two-factor authentication"
"tfaTwoFactorAuth": "Two-factor authentication",
"ublogCommunity": "Community",
"ublogXBlog": "{param}'s Blog"
}
+3 -1
View File
@@ -1698,5 +1698,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{antaŭ {count} jaro} other{antaŭ {count} jaroj}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{restas {count} minuto} other{restas {count} minutoj}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} horo restas} other{{count} horoj restas}}",
"tfaTwoFactorAuth": "Dufaza aŭtentigo"
"tfaTwoFactorAuth": "Dufaza aŭtentigo",
"ublogCommunity": "Komunumo",
"ublogXBlog": "Blogo de {param}"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{hace {count} año} other{hace {count} años}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minuto restante} other{{count} minutos restantes}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} hora restante} other{{count} horas restantes}}",
"tfaTwoFactorAuth": "Autenticación en dos pasos"
"tfaTwoFactorAuth": "Autenticación en dos pasos",
"ublogCommunity": "Comunidad",
"ublogXBlog": "Blog de {param}"
}
+2 -1
View File
@@ -1390,5 +1390,6 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} aasta tagasi} other{{count} aastat tagasi}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minut jäänud} other{{count} minutit jäänud}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} tund jäänud} other{{count} tundi jäänud}}",
"tfaTwoFactorAuth": "Kaheastmeline autentimine"
"tfaTwoFactorAuth": "Kaheastmeline autentimine",
"ublogXBlog": "Kasutaja {param} blogi"
}
+3 -1
View File
@@ -1825,5 +1825,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{orain dela urte {count}} other{orain dela {count} urte}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{Minutu {count} falta da} other{{count} minutu falta dira}}",
"timeagoNbHoursRemaining": "{count, plural, =1{Ordu {count} falta da} other{{count} ordu falta dira}}",
"tfaTwoFactorAuth": "Bi faktoreko autentifikazioa"
"tfaTwoFactorAuth": "Bi faktoreko autentifikazioa",
"ublogCommunity": "Komunitatea",
"ublogXBlog": "{param} erabiltzailearen Bloga"
}
+3 -1
View File
@@ -1819,5 +1819,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} سال پیش} other{{count} سال پیش}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} دقیقه باقی مانده} other{{count} دقیقه باقی مانده}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} ساعت باقی مانده} other{{count} ساعت باقی مانده}}",
"tfaTwoFactorAuth": "راستین‌آزمایی دوعاملی"
"tfaTwoFactorAuth": "راستین‌آزمایی دوعاملی",
"ublogCommunity": "همدارگان",
"ublogXBlog": "وبنوشتِ {param}"
}
+3 -1
View File
@@ -1826,5 +1826,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} vuosi sitten} other{{count} vuotta sitten}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minuutti jäljellä} other{{count} minuuttia jäljellä}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} tunti jäljellä} other{{count} tuntia jäljellä}}",
"tfaTwoFactorAuth": "Kaksivaiheinen tunnistautuminen"
"tfaTwoFactorAuth": "Kaksivaiheinen tunnistautuminen",
"ublogCommunity": "Yhteisön blogit",
"ublogXBlog": "Käyttäjän {param} blogi"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{il y a {count} an} other{il y a {count} ans}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minute restante} other{{count} minutes restantes}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} heure restante} other{{count} heures restantes}}",
"tfaTwoFactorAuth": "Authentification à deux facteurs"
"tfaTwoFactorAuth": "Authentification à deux facteurs",
"ublogCommunity": "Communauté",
"ublogXBlog": "Blogue de {param}"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{hai {count} ano} other{hai {count} anos}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minuto restante} other{{count} minutos restantes}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} hora restante} other{{count} horas restantes}}",
"tfaTwoFactorAuth": "Autenticación en dous pasos"
"tfaTwoFactorAuth": "Autenticación en dous pasos",
"ublogCommunity": "Comunidade",
"ublogXBlog": "O Blog de {param}"
}
+3 -1
View File
@@ -1825,5 +1825,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{vor {count} Jahr} other{vor {count} Jahr}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} Minute blibt} other{{count} Minute blibed}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} Schtund blibt} other{{count} Schtunde blibed}}",
"tfaTwoFactorAuth": "Zwei-Faktor-Autentifizierig"
"tfaTwoFactorAuth": "Zwei-Faktor-Autentifizierig",
"ublogCommunity": "G'meinschaft",
"ublogXBlog": "{param}'s Blog"
}
+2 -1
View File
@@ -1664,5 +1664,6 @@
"timeagoNbYearsAgo": "{count, plural, =1{לפני שנה {count}} =2{לפני {count} שנים} many{לפני {count} שנים} other{לפני {count} שנים}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{דקה {count} נותרה} =2{{count} דקות נותרו} many{{count} דקות נותרו} other{{count} דקות נותרו}}",
"timeagoNbHoursRemaining": "{count, plural, =1{שעה {count} נותרה} =2{{count} שעות נותרו} many{{count} שעות נותרו} other{{count} שעות נותרו}}",
"tfaTwoFactorAuth": "אימות דו־שלבי"
"tfaTwoFactorAuth": "אימות דו־שלבי",
"ublogXBlog": "הבלוג של {param}"
}
+2 -1
View File
@@ -1503,5 +1503,6 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} वर्ष पहले} other{{count} वर्षों पहले}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} मिनट बचा है} other{{count} मिनट बचे हैं}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} घंटा बचा है} other{{count} घंटे बचे हैं}}",
"tfaTwoFactorAuth": "दो-चरण प्रमाणीकरण"
"tfaTwoFactorAuth": "दो-चरण प्रमाणीकरण",
"ublogXBlog": "{param} का ब्लॉग"
}
+2 -1
View File
@@ -1600,5 +1600,6 @@
"timeagoNbYearsAgo": "{count, plural, =1{prije {count} godinu} few{prije {count} godine} other{prije {count} godina}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{Preostala {count} minuta} few{Preostale {count} minute} other{Preostalo {count} minuta}}",
"timeagoNbHoursRemaining": "{count, plural, =1{Preostao {count} sat} few{Preostala {count} sata} other{Preostalo {count} sati}}",
"tfaTwoFactorAuth": "Dvofaktorska provjera autentičnosti"
"tfaTwoFactorAuth": "Dvofaktorska provjera autentičnosti",
"ublogXBlog": "Blog korisnika {param}"
}
+3 -1
View File
@@ -1807,5 +1807,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} éve} other{{count} éve}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} perc van hátra} other{{count} perc van hátra}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} óra van hátra} other{{count} óra van hátra}}",
"tfaTwoFactorAuth": "Kétlépcsős azonosítás"
"tfaTwoFactorAuth": "Kétlépcsős azonosítás",
"ublogCommunity": "Közösség",
"ublogXBlog": "{param} blogja"
}
+2 -1
View File
@@ -1449,5 +1449,6 @@
"timeagoNbWeeksAgo": "{count, plural, =1{{count} շաբաթ առաջ} other{{count} շաբաթ առաջ}}",
"timeagoNbMonthsAgo": "{count, plural, =1{{count} ամիս առաջ} other{{count} ամիս առաջ}}",
"timeagoNbYearsAgo": "{count, plural, =1{{count} տարի առաջ} other{{count} տարի առաջ}}",
"tfaTwoFactorAuth": "Երկգործոն նույնականացում"
"tfaTwoFactorAuth": "Երկգործոն նույնականացում",
"ublogXBlog": "{param}-ի բլոգ"
}
+2 -1
View File
@@ -1599,5 +1599,6 @@
"timeagoNbYearsAgo": "{count, plural, other{{count} tahun yang lalu}}",
"timeagoNbMinutesRemaining": "{count, plural, other{{count} menit tersisa}}",
"timeagoNbHoursRemaining": "{count, plural, other{{count} jam tersisa}}",
"tfaTwoFactorAuth": "Autentikasi dua-langkah"
"tfaTwoFactorAuth": "Autentikasi dua-langkah",
"ublogXBlog": "Blog dari {param}"
}
+3 -1
View File
@@ -1825,5 +1825,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} anno fa} other{{count} anni fa}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minuto rimanente} other{{count} minuti rimanenti}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} ora rimanente} other{{count} ore rimanenti}}",
"tfaTwoFactorAuth": "Autenticazione a due fattori"
"tfaTwoFactorAuth": "Autenticazione a due fattori",
"ublogCommunity": "Comunità",
"ublogXBlog": "Blog di {param}"
}
+3 -1
View File
@@ -1825,5 +1825,7 @@
"timeagoNbYearsAgo": "{count, plural, other{{count} 年前}}",
"timeagoNbMinutesRemaining": "{count, plural, other{残り {count} 分}}",
"timeagoNbHoursRemaining": "{count, plural, other{残り {count} 時間}}",
"tfaTwoFactorAuth": "2 要素認証"
"tfaTwoFactorAuth": "2 要素認証",
"ublogCommunity": "コミュニティ",
"ublogXBlog": "{param} のブログ"
}
+2 -1
View File
@@ -1589,5 +1589,6 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} жыл бұрын} other{{count} жыл бұрын}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} минут қалды} other{{count} минут қалды}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} сағат қалды} other{{count} сағат қалды}}",
"tfaTwoFactorAuth": "Екісатылы өкіл-растау"
"tfaTwoFactorAuth": "Екісатылы өкіл-растау",
"ublogXBlog": "{param} блогі"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, other{{count}년 전}}",
"timeagoNbMinutesRemaining": "{count, plural, other{{count}분 남음}}",
"timeagoNbHoursRemaining": "{count, plural, other{{count}시간 남음}}",
"tfaTwoFactorAuth": "2단계 인증"
"tfaTwoFactorAuth": "2단계 인증",
"ublogCommunity": "커뮤니티",
"ublogXBlog": "{param}의 블로그"
}
+2 -1
View File
@@ -1639,5 +1639,6 @@
"timeagoNbYearsAgo": "{count, plural, =1{Prieš {count} metus} few{Prieš {count} metus} many{Prieš {count} metų} other{Prieš {count} metų}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{Liko {count} minutė} few{Liko {count} minutės} many{Liko {count} minučių} other{Liko {count} minučių}}",
"timeagoNbHoursRemaining": "{count, plural, =1{Liko {count} valanda} few{Liko {count} valandos} many{Liko {count} valandų} other{Liko {count} valandų}}",
"tfaTwoFactorAuth": "Dviejų lygių tapatumo nustatymas"
"tfaTwoFactorAuth": "Dviejų lygių tapatumo nustatymas",
"ublogXBlog": "{param} Tinklaraštis"
}
+2 -1
View File
@@ -1484,5 +1484,6 @@
"timeagoNbWeeksAgo": "{count, plural, =0{pirms {count} nedēļām} =1{pirms {count} nedēļas} other{pirms {count} nedēļām}}",
"timeagoNbMonthsAgo": "{count, plural, =0{pirms {count} mēnešiem} =1{pirms {count} mēneša} other{pirms {count} mēnešiem}}",
"timeagoNbYearsAgo": "{count, plural, =0{pirms {count} gadiem} =1{pirms {count} gada} other{pirms {count} gadiem}}",
"tfaTwoFactorAuth": "Divfaktoru autentifikācija"
"tfaTwoFactorAuth": "Divfaktoru autentifikācija",
"ublogXBlog": "Lietotāja {param} blogs"
}
+3 -1
View File
@@ -1825,5 +1825,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{for {count} år siden} other{for {count} år siden}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minutt igjen} other{{count} minutter igjen}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} time igjen} other{{count} timer igjen}}",
"tfaTwoFactorAuth": "Tofaktorautentisering"
"tfaTwoFactorAuth": "Tofaktorautentisering",
"ublogCommunity": "Fellesskap",
"ublogXBlog": "Bloggen til {param}"
}
+3 -1
View File
@@ -1825,5 +1825,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} jaar geleden} other{{count} jaar geleden}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minuut resterend} other{{count} minuten resterend}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} uur resterend} other{{count} uur resterend}}",
"tfaTwoFactorAuth": "Tweestapsverificatie"
"tfaTwoFactorAuth": "Tweestapsverificatie",
"ublogCommunity": "Gemeenschap",
"ublogXBlog": "Blog van {param}"
}
+3 -1
View File
@@ -1824,5 +1824,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} rok temu} few{{count} lata temu} many{{count} lat temu} other{{count} lat temu}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{Pozostała {count} minuta} few{Pozostały {count} minuty} many{Pozostało {count} minut} other{Pozostało {count} minut}}",
"timeagoNbHoursRemaining": "{count, plural, =1{Pozostała {count} godzina} few{Pozostały {count} godziny} many{Pozostało {count} godzin} other{Pozostało {count} godzin}}",
"tfaTwoFactorAuth": "Uwierzytelnianie dwuskładnikowe"
"tfaTwoFactorAuth": "Uwierzytelnianie dwuskładnikowe",
"ublogCommunity": "Społeczność",
"ublogXBlog": "Blog gracza {param}"
}
+3 -1
View File
@@ -1782,5 +1782,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{há {count} ano} other{há {count} anos}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minuto restante} other{{count} minutos restantes}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} hora restante} other{{count} horas restantes}}",
"tfaTwoFactorAuth": "Autenticação de dois fatores"
"tfaTwoFactorAuth": "Autenticação de dois fatores",
"ublogCommunity": "Comunidade",
"ublogXBlog": "Blog de {param}"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} ano atrás} other{{count} anos atrás}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minuto restante} other{{count} minutos restantes}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} hora restante} other{{count} horas restantes}}",
"tfaTwoFactorAuth": "Autenticação de dois fatores"
"tfaTwoFactorAuth": "Autenticação de dois fatores",
"ublogCommunity": "Comunidade",
"ublogXBlog": "Blog do(a) {param}"
}
+3 -1
View File
@@ -1825,5 +1825,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{cu un an în urmă} few{cu {count} ani în urmă} other{cu {count} de ani în urmă}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{un minut rămas} few{{count} minute rămase} other{{count} de minute rămase}}",
"timeagoNbHoursRemaining": "{count, plural, =1{o oră rămasă} few{{count} ore rămase} other{{count} de ore rămase}}",
"tfaTwoFactorAuth": "Autentificare în doi pași"
"tfaTwoFactorAuth": "Autentificare în doi pași",
"ublogCommunity": "Comunitate",
"ublogXBlog": "Blog-ul lui {param}"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} год назад} few{{count} года назад} many{{count} лет назад} other{{count} лет назад}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{осталась {count} минута} few{осталось {count} минуты} many{осталось {count} минут} other{осталось {count} минут}}",
"timeagoNbHoursRemaining": "{count, plural, =1{остался {count} час} few{осталось {count} часа} many{осталось {count} часов} other{осталось {count} часов}}",
"tfaTwoFactorAuth": "Двухфакторная аутентификация"
"tfaTwoFactorAuth": "Двухфакторная аутентификация",
"ublogCommunity": "Сообщество",
"ublogXBlog": "Блог {param}"
}
+3 -1
View File
@@ -1760,5 +1760,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{pred {count} rokom} few{pred {count} rokmi} many{pred {count} rokmi} other{pred {count} rokmi}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{ostáva {count} minúta} few{ostávajú {count} minúty} many{ostáva {count} minút} other{ostáva {count} minút}}",
"timeagoNbHoursRemaining": "{count, plural, =1{ostáva {count} hodina} few{ostávajú {count} hodiny} many{ostáva {count} hodín} other{ostáva {count} hodín}}",
"tfaTwoFactorAuth": "Dvojstupňové overenie"
"tfaTwoFactorAuth": "Dvojstupňové overenie",
"ublogCommunity": "Komunita",
"ublogXBlog": "Blog užívateľa {param}"
}
+3 -1
View File
@@ -1820,5 +1820,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{Pred {count} letom} =2{Pred {count} letoma} few{Pred {count} leti} other{Pred {count} leti}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{še {count} minuta} =2{še {count} minuti} few{še {count} minute} other{še {count} minut}}",
"timeagoNbHoursRemaining": "{count, plural, =1{še {count} ura} =2{še {count} uri} few{še {count} ure} other{še {count} ur}}",
"tfaTwoFactorAuth": "Dvojna avtentikacija"
"tfaTwoFactorAuth": "Dvojna avtentikacija",
"ublogCommunity": "Skupnost",
"ublogXBlog": "{param} blog"
}
+3 -1
View File
@@ -1778,5 +1778,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} vit më parë} other{para {count} viteve}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{Edhe {count} minutë} other{Edhe {count} minuta}}",
"timeagoNbHoursRemaining": "{count, plural, =1{Edhe {count} orë} other{Edhe {count} orë}}",
"tfaTwoFactorAuth": "Mirëfilltësim dyfaktorësh"
"tfaTwoFactorAuth": "Mirëfilltësim dyfaktorësh",
"ublogCommunity": "Bashkësi",
"ublogXBlog": "Blogu i {param}"
}
+3 -1
View File
@@ -1694,5 +1694,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} år sedan} other{{count} år sedan}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} minut återstår} other{{count} minuter återstår}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} timme återstår} other{{count} timmar återstår}}",
"tfaTwoFactorAuth": "Tvåfaktorsautentisering"
"tfaTwoFactorAuth": "Tvåfaktorsautentisering",
"ublogCommunity": "Community",
"ublogXBlog": "{param}s blogg"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} yıl önce} other{{count} yıl önce}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} dakika kaldı} other{{count} dakika kaldı}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} saat kaldı} other{{count} saat kaldı}}",
"tfaTwoFactorAuth": "İki faktörlü kimlik doğrulama"
"tfaTwoFactorAuth": "İki faktörlü kimlik doğrulama",
"ublogCommunity": "Topluluk",
"ublogXBlog": "{param} Bloğu"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} рік тому} few{{count} роки тому} many{{count} років тому} other{{count} року тому}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{залишилася {count} хвилина} few{залишилося {count} хвилини} many{залишилося {count} хвилин} other{залишилося {count} хвилини}}",
"timeagoNbHoursRemaining": "{count, plural, =1{залишилася {count} година} few{залишилося {count} години} many{залишилося {count} годин} other{залишилося {count} години}}",
"tfaTwoFactorAuth": "Двофакторна автентифікація"
"tfaTwoFactorAuth": "Двофакторна автентифікація",
"ublogCommunity": "Спільнота",
"ublogXBlog": "Блог {param}"
}
+3 -1
View File
@@ -1825,5 +1825,7 @@
"timeagoNbYearsAgo": "{count, plural, =1{{count} yil oldin} other{{count} yil oldin}}",
"timeagoNbMinutesRemaining": "{count, plural, =1{{count} daqiqa qoldi} other{{count} daqiqa qoldi}}",
"timeagoNbHoursRemaining": "{count, plural, =1{{count} soat qoldi} other{{count} soat qoldi}}",
"tfaTwoFactorAuth": "Ikki bosqishli autentifikatsiya"
"tfaTwoFactorAuth": "Ikki bosqishli autentifikatsiya",
"ublogCommunity": "Hamjamiyat",
"ublogXBlog": "{param} blogi"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, other{{count} năm trước}}",
"timeagoNbMinutesRemaining": "{count, plural, other{còn {count} phút}}",
"timeagoNbHoursRemaining": "{count, plural, other{còn {count} giờ}}",
"tfaTwoFactorAuth": "Xác thực 2 bước"
"tfaTwoFactorAuth": "Xác thực 2 bước",
"ublogCommunity": "Cộng đồng",
"ublogXBlog": "Bài viết của {param}"
}
+3 -1
View File
@@ -1827,5 +1827,7 @@
"timeagoNbYearsAgo": "{count, plural, other{{count} 年前}}",
"timeagoNbMinutesRemaining": "{count, plural, other{剩余 {count} 分钟}}",
"timeagoNbHoursRemaining": "{count, plural, other{剩余 {count} 小时}}",
"tfaTwoFactorAuth": "双重认证"
"tfaTwoFactorAuth": "双重认证",
"ublogCommunity": "社区",
"ublogXBlog": "{param} 的博客"
}
+3 -1
View File
@@ -1677,5 +1677,7 @@
"timeagoNbYearsAgo": "{count, plural, other{{count}年前}}",
"timeagoNbMinutesRemaining": "{count, plural, other{剩下 {count} 分鐘}}",
"timeagoNbHoursRemaining": "{count, plural, other{剩下 {count} 小時}}",
"tfaTwoFactorAuth": "兩步驟驗證"
"tfaTwoFactorAuth": "兩步驟驗證",
"ublogCommunity": "社群",
"ublogXBlog": "{param}的部落格"
}
+12
View File
@@ -11169,6 +11169,18 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Two-factor authentication'**
String get tfaTwoFactorAuth;
/// No description provided for @ublogCommunity.
///
/// In en, this message translates to:
/// **'Community'**
String get ublogCommunity;
/// No description provided for @ublogXBlog.
///
/// In en, this message translates to:
/// **'{param}\'s Blog'**
String ublogXBlog(String param);
}
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsAf extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Tweeledige verifikasie';
@override
String get ublogCommunity => 'Community';
@override
String ublogXBlog(String param) {
return '$param se webjoernaal';
}
}
+8
View File
@@ -6942,4 +6942,12 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'التوثيق ذو العاملين';
@override
String get ublogCommunity => 'المجتمع';
@override
String ublogXBlog(String param) {
return 'مدونة $param';
}
}
+8
View File
@@ -6560,4 +6560,12 @@ class AppLocalizationsAz extends AppLocalizations {
@override
String get tfaTwoFactorAuth => '2 mərhələli təsdiqləmə';
@override
String get ublogCommunity => 'Community';
@override
String ublogXBlog(String param) {
return '$param\'s Blog';
}
}
+8
View File
@@ -6740,4 +6740,12 @@ class AppLocalizationsBe extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Двухфактарная аўтэнтыфікацыя';
@override
String get ublogCommunity => 'Community';
@override
String ublogXBlog(String param) {
return 'Блог $param';
}
}
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Двуфакторно удостоверяване';
@override
String get ublogCommunity => 'Общност';
@override
String ublogXBlog(String param) {
return 'Блогът на $param';
}
}
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsBn extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'টু-ফ্যাক্টর অথেন্টিকেশন';
@override
String get ublogCommunity => 'Community';
@override
String ublogXBlog(String param) {
return '$param\'s Blog';
}
}
+8
View File
@@ -6657,4 +6657,12 @@ class AppLocalizationsBs extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Dvofaktorska provjera autentičnosti';
@override
String get ublogCommunity => 'Zajednica';
@override
String ublogXBlog(String param) {
return 'Blog korisnika $param';
}
}
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsCa extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Autenticació de dos factors';
@override
String get ublogCommunity => 'Comunitat';
@override
String ublogXBlog(String param) {
return 'Blog de $param';
}
}
+8
View File
@@ -6750,4 +6750,12 @@ class AppLocalizationsCs extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Dvoufázové ověření';
@override
String get ublogCommunity => 'Komunita';
@override
String ublogXBlog(String param) {
return 'Blog hráče $param';
}
}
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsDa extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'To-faktor-godkendelse';
@override
String get ublogCommunity => 'Fællesskab';
@override
String ublogXBlog(String param) {
return 'Blog - $param';
}
}
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Zwei-Faktor-Authentifizierung';
@override
String get ublogCommunity => 'Gemeinschaft';
@override
String ublogXBlog(String param) {
return 'Blog von $param';
}
}
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsEl extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Έλεγχος ταυτότητας δύο παραγόντων';
@override
String get ublogCommunity => 'Κοινότητα';
@override
String ublogXBlog(String param) {
return 'Ιστολόγιο του χρήστη $param';
}
}
+16
View File
@@ -6560,6 +6560,14 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Two-factor authentication';
@override
String get ublogCommunity => 'Community';
@override
String ublogXBlog(String param) {
return '$param\'s Blog';
}
}
/// The translations for English, as used in the United States (`en_US`).
@@ -13120,4 +13128,12 @@ class AppLocalizationsEnUs extends AppLocalizationsEn {
@override
String get tfaTwoFactorAuth => 'Two-factor authentication';
@override
String get ublogCommunity => 'Community';
@override
String ublogXBlog(String param) {
return '$param\'s Blog';
}
}
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsEo extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Dufaza aŭtentigo';
@override
String get ublogCommunity => 'Komunumo';
@override
String ublogXBlog(String param) {
return 'Blogo de $param';
}
}
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Autenticación en dos pasos';
@override
String get ublogCommunity => 'Comunidad';
@override
String ublogXBlog(String param) {
return 'Blog de $param';
}
}
+8
View File
@@ -6562,4 +6562,12 @@ class AppLocalizationsEt extends AppLocalizations {
@override
String get tfaTwoFactorAuth => 'Kaheastmeline autentimine';
@override
String get ublogCommunity => 'Community';
@override
String ublogXBlog(String param) {
return 'Kasutaja $param blogi';
}
}

Some files were not shown because too many files have changed in this diff Show More