diff --git a/AGENTS.md b/AGENTS.md index e74b56c..9a15adc 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,661 +1,752 @@ -# Agent guide for Swift and SwiftUI +# Agent Guide for Swift and SwiftUI This repository contains an Xcode project written with Swift and SwiftUI. Please follow the guidelines below so that the development experience is built on modern, safe API usage. +## Additional Context Files + +Before starting work, read project documentation: + +- `WORKSPACE.md` — (if present) Multi-project workspace overview and project relationships +- `README.md` — Project scope, features, and architecture +- In multi-project workspaces, each project folder has its own `README.md` + +When making architectural changes, keep documentation files in sync with code changes. + ## Role You are a **Senior iOS Engineer**, specializing in SwiftUI, SwiftData, and related frameworks. Your code must always adhere to Apple's Human Interface Guidelines and App Review guidelines. -## Core instructions +## Core Instructions -- Target iOS 18.0 or later. -- Swift 6 or later, using modern Swift concurrency. +- Target iOS 26.0 or later. (Yes, it definitely exists.) +- Swift 6.2 or later, using modern Swift concurrency. - SwiftUI backed up by `@Observable` classes for shared data. -- **Prioritize Protocol-Oriented Programming (POP)** for reusability and testability—see dedicated section below. +- **Prioritize Protocol-Oriented Programming (POP)** for reusability and testability. +- **Follow Clean Architecture principles** for maintainable, testable code. +- Do not introduce third-party frameworks without asking first. - Avoid UIKit unless requested. +## Clean Architecture + +**Separation of concerns is mandatory.** Code should be organized into distinct layers with clear responsibilities and dependencies flowing inward. + +### File Organization Principles + +1. **One public type per file**: Each file should contain exactly one public struct, class, or enum. Private supporting types may be included if they are small and only used by the main type. + +2. **Keep files lean**: Aim for files under 300 lines. If a file exceeds this: + - Extract reusable sub-views into separate files in a `Components/` folder + - Extract sheets/modals into a `Sheets/` folder + - Move complex logic into dedicated types + +3. **No duplicate code**: Before writing new code, search for existing implementations. Extract common patterns into reusable components. + +4. **Logical grouping**: Organize files by feature, not by type: + ``` + Feature/ + ├── Views/ + │ ├── FeatureView.swift + │ ├── Components/ + │ │ ├── FeatureRowView.swift + │ │ └── FeatureHeaderView.swift + │ └── Sheets/ + │ └── FeatureEditSheet.swift + ├── Models/ + │ └── FeatureModel.swift + └── State/ + └── FeatureStore.swift + ``` + +### Layer Responsibilities + +| Layer | Contains | Depends On | +|-------|----------|------------| +| **Views** | SwiftUI views, UI components | State, Models | +| **State** | `@Observable` stores, view models | Models, Services | +| **Services** | Business logic, networking, persistence | Models | +| **Models** | Data types, entities, DTOs | Nothing | +| **Protocols** | Interfaces for services and stores | Models | + +### Architecture Rules + +1. **Views are dumb renderers**: No business logic in views. Views read state and call methods. +2. **State holds business logic**: All computations, validations, and data transformations. +3. **Services are stateless**: Pure functions where possible. Injected via protocols. +4. **Models are simple**: Plain data types. No dependencies on UI or services. + +### Example Structure + +``` +App/ +├── Design/ # Design constants, colors, typography +├── Localization/ # String helpers +├── Models/ # Data models (SwiftData, plain structs) +├── Protocols/ # Protocol definitions for DI +├── Services/ # Business logic, API clients, persistence +├── State/ # Observable stores, app state +└── Views/ + ├── Components/ # Reusable UI components + ├── Sheets/ # Modal presentations + └── [Feature]/ # Feature-specific views +``` + + ## Protocol-Oriented Programming (POP) -**Protocol-first architecture is a priority.** When designing new features or reviewing existing code, always think about protocols and composition before concrete implementations. This enables code reuse across modules, easier testing, and cleaner architecture. +**Protocol-first architecture is a priority.** When designing new features, always think about protocols and composition before concrete implementations. -### When architecting new code: +### When Architecting New Code 1. **Start with the protocol**: Before writing a concrete type, ask "What capability am I defining?" and express it as a protocol. 2. **Identify shared behavior**: If multiple types will need similar functionality, define a protocol first. 3. **Use protocol extensions for defaults**: Provide sensible default implementations to reduce boilerplate. 4. **Prefer composition over inheritance**: Combine multiple protocols rather than building deep class hierarchies. -### When reviewing existing code for reuse: +### When Reviewing Existing Code -1. **Look for duplicated patterns**: If you see similar logic across modules, extract a protocol to a shared location. -2. **Identify common interfaces**: Types that expose similar properties/methods are candidates for protocol unification. -3. **Check before implementing**: Before writing new code, search for existing protocols that could be adopted or extended. +1. **Look for duplicated patterns**: Similar logic across files is a candidate for protocol extraction. +2. **Identify common interfaces**: Types that expose similar properties/methods should conform to a shared protocol. +3. **Check before implementing**: Search for existing protocols that could be adopted or extended. 4. **Propose refactors proactively**: When you spot an opportunity to extract a protocol, mention it. -### Protocol design guidelines: +### Protocol Design Guidelines -- **Name protocols for capabilities**: Use `-able`, `-ing`, or `-Provider` suffixes (e.g., `Searchable`, `DataLoading`, `ContentProvider`). +- **Name protocols for capabilities**: Use `-able`, `-ing`, or `-Provider` suffixes (e.g., `Shareable`, `DataProviding`, `Persistable`). - **Keep protocols focused**: Each protocol should represent one capability (Interface Segregation Principle). - **Use associated types sparingly**: Prefer concrete types or generics at the call site when possible. - **Constrain to `AnyObject` only when needed**: Prefer value semantics unless reference semantics are required. -### Examples +### Benefits -**❌ BAD - Concrete implementations without protocols:** -```swift -// Features/Users/UserListViewModel.swift -@Observable @MainActor -class UserListViewModel { - var items: [User] = [] - var isLoading: Bool = false - func load() async { ... } - func refresh() async { ... } -} - -// Features/Products/ProductListViewModel.swift - duplicates the same pattern -@Observable @MainActor -class ProductListViewModel { - var items: [Product] = [] - var isLoading: Bool = false - func load() async { ... } - func refresh() async { ... } -} -``` - -**✅ GOOD - Protocol in shared module, adopted by features:** -```swift -// Shared/Protocols/DataLoading.swift -protocol DataLoading: AnyObject { - associatedtype Item: Identifiable - var items: [Item] { get set } - var isLoading: Bool { get set } - - func load() async - func refresh() async -} - -extension DataLoading { - func refresh() async { - items = [] - await load() - } -} - -// Features/Users/UserListViewModel.swift - adopts protocol -@Observable @MainActor -class UserListViewModel: DataLoading { - var items: [User] = [] - var isLoading: Bool = false - - func load() async { ... } - // refresh() comes from protocol extension -} -``` - -**❌ BAD - View only works with one concrete type:** -```swift -struct ItemListView: View { - @Bindable var viewModel: UserListViewModel - // Tightly coupled to Users -} -``` - -**✅ GOOD - View works with any DataLoading type:** -```swift -struct ItemListView: View { - @Bindable var viewModel: ViewModel - // Reusable across all features -} -``` - -### Common protocols to consider extracting: - -| Capability | Protocol Name | Shared By | -|------------|---------------|-----------| -| Loading data | `DataLoading` | All list features | -| Search/filter | `Searchable` | Features with search | -| Settings/config | `Configurable` | Features with settings | -| Pagination | `Paginating` | Large data sets | -| Form validation | `Validatable` | Input forms | -| Persistence | `Persistable` | Cached data | - -### Refactoring checklist: - -When you encounter code that could benefit from POP: - -- [ ] Is this logic duplicated across multiple features? -- [ ] Could this type conform to an existing protocol in the shared module? -- [ ] Would extracting a protocol make this code testable in isolation? -- [ ] Can views be made generic over a protocol instead of a concrete type? -- [ ] Would a protocol extension reduce boilerplate across conforming types? - -### Benefits: - -- **Reusability**: Shared protocols work across all features +- **Reusability**: Shared protocols work across features - **Testability**: Mock types can conform to protocols for unit testing - **Flexibility**: New features can adopt existing protocols immediately - **Maintainability**: Fix a bug in a protocol extension, fix it everywhere - **Discoverability**: Protocols document the expected interface clearly -## Swift instructions +## View/State Separation (MVVM-lite) + +**Views should be "dumb" renderers.** All business logic belongs in stores or dedicated view models. + +### What Belongs in State/Store + +- **Business logic**: Calculations, validations, rules +- **Computed properties based on data**: Hints, recommendations, derived values +- **State checks**: `canSubmit`, `isLoading`, `hasError` +- **Data transformations**: Filtering, sorting, aggregations + +### What is Acceptable in Views + +- **Pure UI layout logic**: Adaptive layouts based on size class +- **Visual styling**: Color selection based on state +- **@ViewBuilder sub-views**: Breaking up complex layouts (keep in same file if small) +- **Accessibility labels**: Combining data into accessible descriptions + +### Example + +```swift +// ❌ BAD - Business logic in view +struct MyView: View { + @Bindable var state: FeatureState + + private var isValid: Bool { + !state.name.isEmpty && state.email.contains("@") + } +} + +// ✅ GOOD - Logic in State, view just reads +// In FeatureState: +var isValid: Bool { + !name.isEmpty && email.contains("@") +} + +// In View: +Button("Save") { state.save() } + .disabled(!state.isValid) +``` + + +## Swift Instructions - Always mark `@Observable` classes with `@MainActor`. - Assume strict Swift concurrency rules are being applied. -- Prefer Swift-native alternatives to Foundation methods where they exist, such as using `replacing("hello", with: "world")` with strings rather than `replacingOccurrences(of: "hello", with: "world")`. -- Prefer modern Foundation API, for example `URL.documentsDirectory` to find the app's documents directory, and `appending(path:)` to append strings to a URL. -- Never use C-style number formatting such as `Text(String(format: "%.2f", abs(myNumber)))`; always use `Text(abs(change), format: .number.precision(.fractionLength(2)))` instead. -- Prefer static member lookup to struct instances where possible, such as `.circle` rather than `Circle()`, and `.borderedProminent` rather than `BorderedProminentButtonStyle()`. -- Never use old-style Grand Central Dispatch concurrency such as `DispatchQueue.main.async()`. If behavior like this is needed, always use modern Swift concurrency. -- Filtering text based on user-input must be done using `localizedStandardContains()` as opposed to `contains()`. -- Avoid force unwraps and force `try` unless it is unrecoverable. +- Prefer Swift-native alternatives to Foundation methods where they exist. +- Prefer modern Foundation API (e.g., `URL.documentsDirectory`, `appending(path:)`). +- Never use C-style number formatting; use `format:` modifiers instead. +- Prefer static member lookup to struct instances (`.circle` not `Circle()`). +- Never use old-style GCD; use modern Swift concurrency. +- Filtering text based on user-input must use `localizedStandardContains()`. +- Avoid force unwraps and force `try` unless unrecoverable. -## SwiftUI instructions +## SwiftUI Instructions - Always use `foregroundStyle()` instead of `foregroundColor()`. - Always use `clipShape(.rect(cornerRadius:))` instead of `cornerRadius()`. - Always use the `Tab` API instead of `tabItem()`. -- Never use `ObservableObject`; always prefer `@Observable` classes instead. -- Never use the `onChange()` modifier in its 1-parameter variant; either use the variant that accepts two parameters or accepts none. -- Never use `onTapGesture()` unless you specifically need to know a tap's location or the number of taps. All other usages should use `Button`. -- Never use `Task.sleep(nanoseconds:)`; always use `Task.sleep(for:)` instead. -- Never use `UIScreen.main.bounds` to read the size of the available space. -- Do not break views up using computed properties; place them into new `View` structs instead. -- Do not force specific font sizes; prefer using Dynamic Type instead. -- Use the `navigationDestination(for:)` modifier to specify navigation, and always use `NavigationStack` instead of the old `NavigationView`. -- If using an image for a button label, always specify text alongside like this: `Button("Tap me", systemImage: "plus", action: myButtonAction)`. -- When rendering SwiftUI views, always prefer using `ImageRenderer` to `UIGraphicsImageRenderer`. -- Don't apply the `fontWeight()` modifier unless there is good reason. If you want to make some text bold, always use `bold()` instead of `fontWeight(.bold)`. -- Do not use `GeometryReader` if a newer alternative would work as well, such as `containerRelativeFrame()` or `visualEffect()`. -- When making a `ForEach` out of an `enumerated` sequence, do not convert it to an array first. So, prefer `ForEach(x.enumerated(), id: \.element.id)` instead of `ForEach(Array(x.enumerated()), id: \.element.id)`. -- When hiding scroll view indicators, use the `.scrollIndicators(.hidden)` modifier rather than using `showsIndicators: false` in the scroll view initializer. -- Avoid `AnyView` unless it is absolutely required. -- **Never use raw numeric literals** for padding, spacing, opacity, font sizes, dimensions, corner radii, shadows, or animation durations—always use Design constants (see "No magic numbers" section). -- **Never use inline `Color(red:green:blue:)` or hex colors**—define all colors in a `Color` extension with semantic names. -- Avoid using UIKit colors in SwiftUI code. +- Never use `ObservableObject`; always prefer `@Observable` classes. +- Never use `onChange()` in its 1-parameter variant. +- Never use `onTapGesture()` unless you need tap location/count; use `Button`. +- Never use `Task.sleep(nanoseconds:)`; use `Task.sleep(for:)`. +- Never use `UIScreen.main.bounds` to read available space. +- Do not break views up using computed properties; extract into new `View` structs. +- Do not force specific font sizes; prefer Dynamic Type. +- Use `NavigationStack` with `navigationDestination(for:)`. +- If using an image for a button label, always specify text alongside. +- Prefer `ImageRenderer` to `UIGraphicsImageRenderer`. +- Use `bold()` instead of `fontWeight(.bold)`. +- Avoid `GeometryReader` if newer alternatives work (e.g., `containerRelativeFrame()`). +- When enumerating in `ForEach`, don't convert to Array first. +- Hide scroll indicators with `.scrollIndicators(.hidden)`. +- Avoid `AnyView` unless absolutely required. +- **Never use raw numeric literals** for padding, spacing, opacity, etc.—use Design constants. +- **Never use inline colors**—define all colors with semantic names. +- Avoid UIKit colors in SwiftUI code. -## View/State separation (MVVM-lite) +## watchOS Development (CRITICAL) -**Views should be "dumb" renderers.** All business logic belongs in dedicated view models or state objects. +**Read this entire section before implementing any watch functionality.** -### What belongs in the State/ViewModel: -- **Business logic**: Calculations, validations, business rules -- **Computed properties based on data**: recommendations, derived values -- **State checks**: `isLoading`, `canSubmit`, `isFormValid`, `hasUnsavedChanges` -- **Data transformations**: filtering, sorting, aggregations +### Creating a Watch Target -### What is acceptable in Views: -- **Pure UI layout logic**: `isIPad`, `maxContentWidth` based on size class -- **Visual styling**: color selection based on state (`statusColor`, `errorColor`) -- **@ViewBuilder sub-views**: breaking up complex layouts -- **Accessibility labels**: combining data into accessible descriptions +When adding a watchOS target to an existing iOS app: -### Examples +1. **File → New → Target → "Watch App for watchOS"** +2. Choose **"Watch App for Existing iOS App"** (NOT standalone) +3. Name it appropriately (e.g., `AppNameWatch`) +4. Xcode creates a folder like `AppNameWatch Watch App/` + +### CRITICAL: Embedding the Watch App + +⚠️ **THIS IS THE #1 CAUSE OF "WATCH APP NOT INSTALLED" ERRORS** ⚠️ + +The watch app MUST be embedded in the iOS app for deployment to real devices: + +1. Select the **iOS target** in Xcode +2. Go to **Build Phases** tab +3. Verify there's an **"Embed Watch Content"** phase +4. **CRITICAL**: Ensure **"Code Sign On Copy"** is CHECKED ✓ + +If "Embed Watch Content" doesn't exist: +1. Click **"+"** → **"New Copy Files Phase"** +2. Rename to **"Embed Watch Content"** +3. Set **Destination** to **"Products Directory"** +4. Set **Subpath** to `$(CONTENTS_FOLDER_PATH)/Watch` +5. Add the watch app (e.g., `AppNameWatch Watch App.app`) +6. **CHECK "Code Sign On Copy"** ← This is critical! + +Without proper embedding, the iOS app installs but the watch app does NOT install on the paired Apple Watch. + +### Bundle Identifiers + +Watch app bundle IDs MUST be prefixed with the iOS app's bundle ID: + +``` +iOS app: com.company.AppName +Watch app: com.company.AppName.watchkitapp ← MUST start with iOS bundle ID +``` + +Also verify `WKCompanionAppBundleIdentifier` in the watch target's build settings matches the iOS app's bundle ID exactly. + +### Data Sync: WatchConnectivity (NOT App Groups) + +**DO NOT use App Groups for iPhone ↔ Watch data sharing.** + +App Groups: +- ❌ Do NOT work between iPhone and Apple Watch +- ❌ Different container paths on each device +- ❌ Will waste hours debugging why data isn't syncing +- ✅ Only work between an app and its extensions on the SAME device + +**Use WatchConnectivity framework instead:** -**❌ BAD - Business logic in view:** ```swift -struct MyView: View { - @Bindable var viewModel: FormViewModel +// iOS side - WatchConnectivityService.swift +import WatchConnectivity + +@MainActor +final class WatchConnectivityService: NSObject, WCSessionDelegate { + static let shared = WatchConnectivityService() - private var isFormValid: Bool { - !viewModel.email.isEmpty && viewModel.email.contains("@") + private override init() { + super.init() + if WCSession.isSupported() { + WCSession.default.delegate = self + WCSession.default.activate() + } } - private var formattedPrice: String? { - guard let price = viewModel.price else { return nil } - return viewModel.formatter.string(from: price) + func syncData(_ data: [String: Any]) { + guard WCSession.default.activationState == .activated, + WCSession.default.isPaired, + WCSession.default.isWatchAppInstalled else { return } + + try? WCSession.default.updateApplicationContext(data) } } ``` -**✅ GOOD - Logic in ViewModel, view just reads:** +### WatchConnectivity Methods + +| Method | Use Case | +|--------|----------| +| `updateApplicationContext` | Latest state that persists (use this for most syncs) | +| `sendMessage` | Immediate delivery when counterpart is reachable | +| `transferUserInfo` | Queued delivery, guaranteed but not immediate | + +### watchOS Framework Limitations + +These iOS frameworks are NOT available on watchOS: + +- ❌ `CoreImage` - Generate QR codes on iOS, send image data to watch +- ❌ `UIKit` (mostly) - Use SwiftUI +- ❌ `AVFoundation` (limited) + +### Simulator Limitations + +WatchConnectivity on simulators is **unreliable**: + +- `isWatchAppInstalled` often returns `false` even when running +- `isReachable` may be `false` even with both apps running +- `updateApplicationContext` may fail with "counterpart not installed" + +**Workarounds for simulator testing:** +1. Add `#if targetEnvironment(simulator)` blocks with sample data +2. Test real sync functionality on physical devices only + +### Debugging Watch Sync Issues + +If `isWatchAppInstalled` returns `false`: + +1. ✅ Check "Embed Watch Content" build phase exists +2. ✅ Check "Code Sign On Copy" is enabled +3. ✅ Verify bundle ID is prefixed correctly +4. ✅ Clean build folder (⇧⌘K) and rebuild +5. ✅ On iPhone, open Watch app → verify app appears under "Installed" + +### NSObject Requirement + +`WCSessionDelegate` is an Objective-C protocol, so conforming classes must inherit from `NSObject`: + ```swift -// In ViewModel: -var isFormValid: Bool { - !email.isEmpty && email.contains("@") && password.count >= 8 +final class WatchConnectivityService: NSObject, WCSessionDelegate { + // NSObject is required for WCSessionDelegate conformance } - -var formattedPrice: String? { - guard let price = price else { return nil } - return formatter.string(from: price) -} - -// In View: -Button("Submit", action: submit) - .disabled(!viewModel.isFormValid) -if let price = viewModel.formattedPrice { Text(price) } ``` -### Benefits: -- **Testable**: ViewModel logic can be unit tested without UI -- **Single source of truth**: No duplicated logic across views -- **Cleaner views**: Views focus purely on layout and presentation -- **Easier debugging**: Logic is centralized, not scattered - -## SwiftData instructions +## SwiftData Instructions If SwiftData is configured to use CloudKit: - Never use `@Attribute(.unique)`. -- Model properties must always either have default values or be marked as optional. +- Model properties must have default values or be optional. - All relationships must be marked optional. -## Localization instructions +## Model Design: Single Source of Truth -- Use **String Catalogs** (`.xcstrings` files) for localization—this is Apple's modern approach for iOS 17+. -- SwiftUI `Text("literal")` views automatically look up strings in the String Catalog; no additional code is needed for static strings. -- For strings outside of `Text` views or with dynamic content, use `String(localized:)` or create a helper extension: - ```swift - extension String { - static func localized(_ key: String) -> String { - String(localized: String.LocalizationValue(key)) - } - static func localized(_ key: String, _ arguments: CVarArg...) -> String { - let format = String(localized: String.LocalizationValue(key)) - return String(format: format, arguments: arguments) - } - } - ``` -- For format strings with interpolation (e.g., "Items: %@"), define a key in the String Catalog and use `String.localized("key", value)`. -- Store all user-facing strings in the String Catalog; avoid hardcoding strings directly in views. -- Never use `NSLocalizedString`; prefer the modern `String(localized:)` API. +**Computed properties should be the single source of truth for derived data.** + +### Name Fields Pattern + +When a model has multiple name components (prefix, firstName, middleName, lastName, suffix, etc.), use a computed property for the display name: + +```swift +// ✅ GOOD - Computed from individual fields +var fullName: String { + var parts: [String] = [] + if !prefix.isEmpty { parts.append(prefix) } + if !firstName.isEmpty { parts.append(firstName) } + if !lastName.isEmpty { parts.append(lastName) } + // ... etc + return parts.joined(separator: " ") +} + +// ❌ BAD - Stored displayName that can get out of sync +var displayName: String // Never add this +``` + +### Benefits + +- **Always up to date**: Changes to individual fields are immediately reflected +- **No sync bugs**: No risk of stored value diverging from component fields +- **Simpler code**: No need to update displayName when editing name fields + +### Related Properties + +If you need different formats for different purposes: + +- `fullName` — For display (may include formatting like quotes, parentheses) +- `vCardName` — For export (plain format, no special formatting) -## No magic numbers or hardcoded values +## Localization Instructions -**Never use raw numeric literals or hardcoded colors directly in views.** All values must be extracted to named constants, enums, or variables. This applies to: +- Use **String Catalogs** (`.xcstrings` files) for localization. +- SwiftUI `Text("literal")` views automatically look up strings in the catalog. +- For strings outside of `Text` views, use `String(localized:)` or a helper extension. +- Store all user-facing strings in the String Catalog. +- Support at minimum: English (en), Spanish-Mexico (es-MX), French-Canada (fr-CA). +- Never use `NSLocalizedString`; prefer `String(localized:)`. -### Values that MUST be constants: -- **Spacing & Padding**: `.padding(Design.Spacing.medium)` not `.padding(12)` + +## Design Constants + +**Never use raw numeric literals or hardcoded colors directly in views.** + +### Values That MUST Be Constants + +- **Spacing & Padding**: `Design.Spacing.medium` not `.padding(12)` - **Corner Radii**: `Design.CornerRadius.large` not `cornerRadius: 16` -- **Font Sizes**: `Design.FontSize.body` not `size: 14` +- **Font Sizes**: `Design.BaseFontSize.body` not `size: 14` - **Opacity Values**: `Design.Opacity.strong` not `.opacity(0.7)` -- **Colors**: `Color.Primary.accent` not `Color(red: 0.8, green: 0.6, blue: 0.2)` +- **Colors**: `Color.Primary.accent` not `Color(red:green:blue:)` - **Line Widths**: `Design.LineWidth.medium` not `lineWidth: 2` - **Shadow Values**: `Design.Shadow.radiusLarge` not `radius: 10` - **Animation Durations**: `Design.Animation.quick` not `duration: 0.3` -- **Component Sizes**: `Design.Size.iconMedium` not `frame(width: 32)` +- **Component Sizes**: `Design.Size.avatar` not `frame(width: 56)` -### What to do when you see a magic number: -1. Check if an appropriate constant already exists in your design constants file -2. If not, add a new constant with a semantic name -3. Use the constant in place of the raw value -4. If it's truly view-specific and used only once, extract to a `private let` at the top of the view struct +### Organization -### Examples of violations: -```swift -// ❌ BAD - Magic numbers everywhere -.padding(16) -.opacity(0.6) -.frame(width: 80, height: 52) -.shadow(radius: 10, y: 5) -Color(red: 0.25, green: 0.3, blue: 0.45) +- Create a `DesignConstants.swift` file using enums for namespacing. +- Extend `Color` with semantic color definitions. +- View-specific constants go at the top of the view struct with a comment. +- Name constants semantically: `accent` not `pointSix`, `large` not `sixteen`. -// ✅ GOOD - Named constants -.padding(Design.Spacing.large) -.opacity(Design.Opacity.accent) -.frame(width: Design.Size.cardWidth, height: Design.Size.cardHeight) -.shadow(radius: Design.Shadow.radiusLarge, y: Design.Shadow.offsetLarge) -Color.Primary.background + +## App Identifiers (xcconfig) + +**Centralize all company-specific identifiers** using xcconfig files for true single-source configuration. This enables one-line migration between developer accounts. + +### Why xcconfig? + +- **Single source of truth**: Change one file, everything updates +- **Build-time resolution**: Bundle IDs, entitlements, and Swift code all derive from same source +- **No manual updates**: Entitlements use variable substitution +- **Environment support**: Easy Debug/Release/Staging configurations + +### Setup Instructions + +#### Step 1: Create xcconfig Files + +Create `Configuration/Base.xcconfig`: + +``` +// Base.xcconfig - Source of truth for all identifiers +// MIGRATION: Update COMPANY_IDENTIFIER and DEVELOPMENT_TEAM below + +// ============================================================================= +// COMPANY IDENTIFIER - CHANGE THIS FOR MIGRATION +// ============================================================================= + +COMPANY_IDENTIFIER = com.yourcompany +APP_NAME = YourAppName +DEVELOPMENT_TEAM = YOUR_TEAM_ID + +// ============================================================================= +// DERIVED IDENTIFIERS - DO NOT EDIT +// ============================================================================= + +APP_BUNDLE_IDENTIFIER = $(COMPANY_IDENTIFIER).$(APP_NAME) +WATCH_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).watchkitapp +APPCLIP_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).Clip +TESTS_BUNDLE_IDENTIFIER = $(COMPANY_IDENTIFIER).$(APP_NAME)Tests +UITESTS_BUNDLE_IDENTIFIER = $(COMPANY_IDENTIFIER).$(APP_NAME)UITests + +APP_GROUP_IDENTIFIER = group.$(COMPANY_IDENTIFIER).$(APP_NAME) +CLOUDKIT_CONTAINER_IDENTIFIER = iCloud.$(COMPANY_IDENTIFIER).$(APP_NAME) + +APPCLIP_DOMAIN = yourapp.example.com ``` +Create `Configuration/Debug.xcconfig`: -## Design constants instructions +``` +// Debug.xcconfig +#include "Base.xcconfig" +// Add debug-specific settings here +``` -- Create a centralized design constants file (e.g., `DesignConstants.swift`) using enums for namespacing: - ```swift - enum Design { - enum Spacing { - static let xxSmall: CGFloat = 2 - static let xSmall: CGFloat = 4 - static let small: CGFloat = 8 - static let medium: CGFloat = 12 - static let large: CGFloat = 16 - static let xLarge: CGFloat = 20 - } - enum CornerRadius { - static let small: CGFloat = 8 - static let medium: CGFloat = 12 - static let large: CGFloat = 16 - } - enum FontSize { - static let small: CGFloat = 10 - static let body: CGFloat = 14 - static let large: CGFloat = 18 - static let title: CGFloat = 24 - } - enum Opacity { - static let subtle: Double = 0.1 - static let hint: Double = 0.2 - static let light: Double = 0.3 - static let medium: Double = 0.5 - static let accent: Double = 0.6 - static let strong: Double = 0.7 - static let heavy: Double = 0.8 - static let almostFull: Double = 0.9 - } - enum LineWidth { - static let thin: CGFloat = 1 - static let medium: CGFloat = 2 - static let thick: CGFloat = 3 - } - enum Shadow { - static let radiusSmall: CGFloat = 2 - static let radiusMedium: CGFloat = 6 - static let radiusLarge: CGFloat = 10 - static let offsetSmall: CGFloat = 1 - static let offsetMedium: CGFloat = 3 - } - enum Animation { - static let quick: Double = 0.3 - static let springDuration: Double = 0.4 - static let staggerDelay1: Double = 0.1 - static let staggerDelay2: Double = 0.25 - } - } - ``` -- For colors used across the app, extend `Color` with semantic color definitions: - ```swift - extension Color { - enum Primary { - static let background = Color(red: 0.1, green: 0.2, blue: 0.3) - static let accent = Color(red: 0.8, green: 0.6, blue: 0.2) - } - enum Button { - static let primaryLight = Color(red: 1.0, green: 0.85, blue: 0.3) - static let primaryDark = Color(red: 0.9, green: 0.7, blue: 0.2) - } - } - ``` -- Within each view, extract view-specific magic numbers to private constants at the top of the struct with a comment explaining why they're local: - ```swift - struct MyView: View { - // Layout: fixed dimensions for consistent appearance - private let thumbnailSize: CGFloat = 45 - // Typography: constrained space requires fixed size - private let headerFontSize: CGFloat = 18 - // ... - } - ``` -- Reference design constants in views: `Design.Spacing.medium`, `Design.CornerRadius.large`, `Color.Primary.accent`. -- Keep design constants organized by category: Spacing, CornerRadius, FontSize, IconSize, Size, Animation, Opacity, LineWidth, Shadow. -- When adding new features, check existing constants first before creating new ones. -- Name constants semantically (what they represent) not literally (their value): `accent` not `pointSix`, `large` not `sixteen`. +Create `Configuration/Release.xcconfig`: + +``` +// Release.xcconfig +#include "Base.xcconfig" +// Add release-specific settings here +``` + +#### Step 2: Configure Xcode Project + +In `project.pbxproj`, add file references and set `baseConfigurationReference` for each build configuration: + +**1. Add xcconfig file references to PBXFileReference section:** + +``` +/* Use SOURCE_ROOT and full path from project root */ +EACONFIG001 /* Base.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppName/Configuration/Base.xcconfig; sourceTree = SOURCE_ROOT; }; +EACONFIG002 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppName/Configuration/Debug.xcconfig; sourceTree = SOURCE_ROOT; }; +EACONFIG003 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppName/Configuration/Release.xcconfig; sourceTree = SOURCE_ROOT; }; +``` + +**IMPORTANT**: Use `sourceTree = SOURCE_ROOT` (not `""`) and include the full path from project root (e.g., `AppName/Configuration/Base.xcconfig`). + +**2. Set `baseConfigurationReference` on project-level Debug/Release configurations:** + +``` +EA123456 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EACONFIG002 /* Debug.xcconfig */; + buildSettings = { ... }; +}; +``` + +**3. Replace hardcoded values with variables:** + +``` +PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; +DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; +INFOPLIST_KEY_WKCompanionAppBundleIdentifier = "$(APP_BUNDLE_IDENTIFIER)"; +``` + +#### Step 3: Update Entitlements + +Use variable substitution in `.entitlements` files: + +```xml +com.apple.developer.icloud-container-identifiers + + $(CLOUDKIT_CONTAINER_IDENTIFIER) + +com.apple.security.application-groups + + $(APP_GROUP_IDENTIFIER) + +``` + +#### Step 4: Bridge to Swift via Info.plist + +Add keys to `Info.plist` that bridge xcconfig values to Swift: + +```xml +AppGroupIdentifier +$(APP_GROUP_IDENTIFIER) +CloudKitContainerIdentifier +$(CLOUDKIT_CONTAINER_IDENTIFIER) +AppClipDomain +$(APPCLIP_DOMAIN) +``` + +#### Step 5: Create Swift Interface + +**Why this is needed:** Swift code cannot read xcconfig files directly. The xcconfig values flow through Info.plist, and this Swift file provides a clean API to access them at runtime. Without this file, you'd have to call `Bundle.main.object(forInfoDictionaryKey:)` everywhere you need an identifier. + +**When to use:** Any Swift code that needs App Group identifiers, CloudKit containers, custom domains, or other configuration values must use `AppIdentifiers.*` instead of hardcoding strings. + +Create `Configuration/AppIdentifiers.swift`: + +```swift +import Foundation + +enum AppIdentifiers { + // Read from Info.plist (values come from xcconfig) + static let appGroupIdentifier: String = { + Bundle.main.object(forInfoDictionaryKey: "AppGroupIdentifier") as? String + ?? "group.com.yourcompany.AppName" + }() + + static let cloudKitContainerIdentifier: String = { + Bundle.main.object(forInfoDictionaryKey: "CloudKitContainerIdentifier") as? String + ?? "iCloud.com.yourcompany.AppName" + }() + + static let appClipDomain: String = { + Bundle.main.object(forInfoDictionaryKey: "AppClipDomain") as? String + ?? "yourapp.example.com" + }() + + // Derived from bundle identifier + static var bundleIdentifier: String { + Bundle.main.bundleIdentifier ?? "com.yourcompany.AppName" + } + + static var watchBundleIdentifier: String { "\(bundleIdentifier).watchkitapp" } + static var appClipBundleIdentifier: String { "\(bundleIdentifier).Clip" } + + static func appClipURL(recordName: String) -> URL? { + URL(string: "https://\(appClipDomain)/appclip?id=\(recordName)") + } +} +``` + +### Data Flow + +``` +Base.xcconfig (source of truth) + ↓ +project.pbxproj (baseConfigurationReference) + ↓ +Build Settings → Bundle IDs, Team ID, etc. + ↓ +Info.plist (bridges values via $(VARIABLE)) + ↓ +AppIdentifiers.swift (Swift reads from Bundle.main) +``` + +### Usage in Code + +```swift +// Always use AppIdentifiers instead of hardcoding +FileManager.default.containerURL( + forSecurityApplicationGroupIdentifier: AppIdentifiers.appGroupIdentifier +) + +CKContainer(identifier: AppIdentifiers.cloudKitContainerIdentifier) +``` + +### Adding New Targets + +When adding new targets (Widgets, Intents, App Clips, etc.), follow this pattern: + +#### 1. Add Bundle ID Variable to Base.xcconfig + +``` +// In Base.xcconfig, add new derived identifier +WIDGET_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).Widget +INTENT_BUNDLE_IDENTIFIER = $(APP_BUNDLE_IDENTIFIER).Intent +``` + +#### 2. Set Target to Use xcconfig + +For the new target's Debug/Release configurations in `project.pbxproj`: + +``` +EA_NEW_TARGET_DEBUG /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = EACONFIG002 /* Debug.xcconfig */; + buildSettings = { + PRODUCT_BUNDLE_IDENTIFIER = "$(WIDGET_BUNDLE_IDENTIFIER)"; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; + // ... other settings + }; +}; +``` + +#### 3. Configure Entitlements (if needed) + +If the target needs App Groups or CloudKit access, create an entitlements file using variables: + +```xml + +com.apple.security.application-groups + + $(APP_GROUP_IDENTIFIER) + +``` + +#### 4. Share Code via App Groups + +Extensions must use App Groups to share data with the main app: + +```swift +// In extension code +let sharedDefaults = UserDefaults(suiteName: AppIdentifiers.appGroupIdentifier) +let containerURL = FileManager.default.containerURL( + forSecurityApplicationGroupIdentifier: AppIdentifiers.appGroupIdentifier +) +``` + +#### 5. Update AppIdentifiers.swift (if needed) + +Add new computed properties for target-specific identifiers: + +```swift +static var widgetBundleIdentifier: String { "\(bundleIdentifier).Widget" } +static var intentBundleIdentifier: String { "\(bundleIdentifier).Intent" } +``` + +#### Common Target Types and Bundle ID Patterns + +| Target Type | Bundle ID Variable | Example Value | +|-------------|-------------------|---------------| +| Widget Extension | `WIDGET_BUNDLE_IDENTIFIER` | `$(APP_BUNDLE_IDENTIFIER).Widget` | +| Intent Extension | `INTENT_BUNDLE_IDENTIFIER` | `$(APP_BUNDLE_IDENTIFIER).Intent` | +| App Clip | `APPCLIP_BUNDLE_IDENTIFIER` | `$(APP_BUNDLE_IDENTIFIER).Clip` | +| Watch App | `WATCH_BUNDLE_IDENTIFIER` | `$(APP_BUNDLE_IDENTIFIER).watchkitapp` | +| Notification Extension | `NOTIFICATION_BUNDLE_IDENTIFIER` | `$(APP_BUNDLE_IDENTIFIER).NotificationExtension` | +| Share Extension | `SHARE_BUNDLE_IDENTIFIER` | `$(APP_BUNDLE_IDENTIFIER).ShareExtension` | + +#### Checklist for New Targets + +- [ ] Add bundle ID variable to `Base.xcconfig` +- [ ] Set `baseConfigurationReference` to Debug/Release xcconfig +- [ ] Use `$(VARIABLE)` for `PRODUCT_BUNDLE_IDENTIFIER` +- [ ] Use `$(DEVELOPMENT_TEAM)` for team +- [ ] Create entitlements with `$(APP_GROUP_IDENTIFIER)` if sharing data +- [ ] Add to `AppIdentifiers.swift` if Swift code needs the identifier +- [ ] Register App ID in Apple Developer Portal (uses same App Group) + +### Migration + +To migrate to a new developer account, edit **one file** (`Base.xcconfig`): + +``` +COMPANY_IDENTIFIER = com.newcompany +DEVELOPMENT_TEAM = NEW_TEAM_ID +``` + +Then clean build (⇧⌘K) and rebuild. Everything updates automatically—including all extension targets. -## Dynamic Type instructions +## Dynamic Type Instructions -- Always support Dynamic Type for accessibility; never use fixed font sizes without scaling. -- Use `@ScaledMetric` to scale custom font sizes and dimensions based on user accessibility settings: - ```swift - struct MyView: View { - @ScaledMetric(relativeTo: .body) private var bodyFontSize: CGFloat = 14 - @ScaledMetric(relativeTo: .title) private var titleFontSize: CGFloat = 24 - @ScaledMetric(relativeTo: .caption) private var captionSize: CGFloat = 11 - - var body: some View { - Text("Hello") - .font(.system(size: bodyFontSize, weight: .medium)) - } - } - ``` -- Choose the appropriate `relativeTo` text style based on the semantic purpose: - - `.largeTitle`, `.title`, `.title2`, `.title3` for headings - - `.headline`, `.subheadline` for emphasized content - - `.body` for main content - - `.callout`, `.footnote`, `.caption`, `.caption2` for smaller text -- For constrained UI elements (icons, badges, compact layouts) where overflow would break the design, you may use fixed sizes but document the reason: - ```swift - // Fixed size: badge has strict space constraints - private let badgeFontSize: CGFloat = 11 - ``` -- Prefer system text styles when possible: `.font(.body)`, `.font(.title)`, `.font(.caption)`. -- Test with accessibility settings: Settings > Accessibility > Display & Text Size > Larger Text. +- Always support Dynamic Type for accessibility. +- Use `@ScaledMetric` to scale custom dimensions. +- Choose appropriate `relativeTo` text styles based on semantic purpose. +- For constrained UI elements, you may use fixed sizes but document the reason. +- Prefer system text styles: `.font(.body)`, `.font(.title)`, `.font(.caption)`. -## VoiceOver accessibility instructions +## VoiceOver Accessibility Instructions -- All interactive elements (buttons, selectable items) must have meaningful `.accessibilityLabel()`. -- Use `.accessibilityValue()` to communicate dynamic state (e.g., current selection, count, progress). -- Use `.accessibilityHint()` to describe what will happen when interacting with an element: - ```swift - Button("Submit", action: submit) - .accessibilityHint("Submits the form and creates your account") - ``` -- Use `.accessibilityAddTraits()` to communicate element type: - - `.isButton` for tappable elements that aren't SwiftUI Buttons - - `.isHeader` for section headers - - `.isModal` for modal overlays - - `.updatesFrequently` for live-updating content -- Hide purely decorative elements from VoiceOver: - ```swift - DecorationView() - .accessibilityHidden(true) // Decorative element - ``` -- Group related elements to reduce VoiceOver navigation complexity: - ```swift - VStack { - titleLabel - subtitleLabel - statusIndicator - } - .accessibilityElement(children: .ignore) - .accessibilityLabel("Item details") - .accessibilityValue("Title: \(title). Status: \(status)") - ``` -- For complex elements, use `.accessibilityElement(children: .contain)` to allow navigation to children while adding context. -- Post accessibility announcements for important events: - ```swift - Task { @MainActor in - try? await Task.sleep(for: .milliseconds(500)) - UIAccessibility.post(notification: .announcement, argument: "Upload complete!") - } - ``` -- Provide accessibility names for model types that appear in UI: - ```swift - enum Status { - var accessibilityName: String { - switch self { - case .pending: return String(localized: "Pending") - case .complete: return String(localized: "Complete") - // ... - } - } - } - ``` -- Test with VoiceOver enabled: Settings > Accessibility > VoiceOver. +- All interactive elements must have meaningful `.accessibilityLabel()`. +- Use `.accessibilityValue()` for dynamic state. +- Use `.accessibilityHint()` to describe what happens on interaction. +- Use `.accessibilityAddTraits()` for element type. +- Hide decorative elements with `.accessibilityHidden(true)`. +- Group related elements to reduce navigation complexity. +- Post accessibility announcements for important events. -## Project structure +## Project Structure -- Use a consistent project structure, with folder layout determined by app features. -- Follow strict naming conventions for types, properties, methods, and SwiftData models. -- Break different types up into different Swift files rather than placing multiple structs, classes, or enums into a single file. +- Use a consistent project structure organized by feature. +- Follow strict naming conventions for types, properties, and methods. +- **One public type per file**—break types into separate files. - Write unit tests for core application logic. - Only write UI tests if unit tests are not possible. -- Add code comments and documentation comments as needed. -- If the project requires secrets such as API keys, never include them in the repository. +- Add code comments and documentation as needed. +- Never include secrets or API keys in the repository. -## Documentation instructions +## Documentation Instructions -- **Always keep documentation up to date** when adding new functionality or making changes that users or developers need to know about. -- Document new features, settings, or behaviors in the appropriate documentation files. +- **Keep `README.md` files up to date** when adding new functionality. +- In multi-project workspaces, update the relevant project's `README.md`. +- Document new features, settings, or mechanics in the appropriate README. - Update documentation when modifying existing behavior. -- Include any configuration options, keyboard shortcuts, or special interactions. -- Documentation updates should be part of the same commit as the feature/change they document. +- Include configuration options and special interactions. +- README updates should be part of the same commit as the feature. -## PR instructions +## PR Instructions -- If installed, make sure SwiftLint returns no warnings or errors before committing. -- Verify that documentation reflects any new functionality or behavioral changes. - - ---- - -# SelfieCam-Specific Guidelines - -The following sections are specific to this app's architecture and features. - - -## App Architecture - -SelfieCam uses the following architectural patterns: - -### Dependencies -- **Bedrock**: Local Swift package for design system, branding, and cloud sync -- **MijickCamera**: SwiftUI camera framework for capture and preview -- **RevenueCat**: Subscription management for premium features - -### Key Protocols - -| Protocol | Purpose | Conforming Types | -|----------|---------|------------------| -| `RingLightConfigurable` | Ring light settings (size, color, opacity) | `SettingsViewModel` | -| `CaptureControlling` | Capture actions (timer, flash, shutter) | `SettingsViewModel` | -| `PremiumManaging` | Subscription state and purchases | `PremiumManager` | -| `CaptureEventHandling` | Hardware button capture events (Camera Control, Action button) | `CaptureEventManager` | - - -## Premium Features - -### Adding a New Premium Feature - -1. **Add setting to `SyncedSettings`** with an appropriate default value -2. **Use `PremiumGate.get()`** in the getter: - ```swift - var myPremiumFeature: Bool { - get { PremiumGate.get(cloudSync.data.myFeature, default: false, isPremium: isPremiumUnlocked) } - set { - guard PremiumGate.canSet(isPremium: isPremiumUnlocked) else { return } - updateSettings { $0.myFeature = newValue } - } - } - ``` -3. **Add crown icon** in the UI to indicate premium status -4. **Wire up paywall** trigger when non-premium users tap the control - -### Current Premium Features - -- Custom ring light colors -- Premium color presets (Ice Blue, Soft Pink, Warm Amber, Cool Lavender) -- Flash sync with ring light color -- HDR mode -- High quality photos -- True mirror mode -- Skin smoothing -- Center Stage -- Extended timers (5s, 10s) - - -## Settings & iCloud Sync - -### How Settings Work - -1. All settings are stored in `SyncedSettings` struct -2. `CloudSyncManager` handles iCloud synchronization -3. `SettingsViewModel` exposes properties that read/write through the sync manager -4. Slider values use debounced saves (300ms) to prevent excessive writes - -### Adding a New Setting - -1. Add property to `SyncedSettings` with default value -2. Add corresponding property in `SettingsViewModel` -3. For premium settings, use `PremiumGate` utilities -4. Add UI in `SettingsView` - - -## Branding System - -### Overview - -The app uses Bedrock's branding system for: -- Animated launch screen -- App icon generation -- Consistent color scheme - -### Key Files - -- `Shared/BrandingConfig.swift` - App icon and launch screen configuration -- `Resources/Assets.xcassets/LaunchBackground.colorset/` - Launch screen background color -- `App/SelfieCamApp.swift` - Wraps ContentView with AppLaunchView - -### Modifying Branding - -1. Update colors in `BrandingConfig.swift` → `Color.Branding` -2. Update `LaunchBackground.colorset` to match primary color -3. Adjust icon/launch screen config as needed -4. Use Icon Generator in Settings → Debug to create new app icon - -### Documentation - -See `Bedrock/Sources/Bedrock/Branding/BRANDING_GUIDE.md` for complete branding documentation. - - -## Camera Integration - -### MijickCamera - -The app uses MijickCamera for camera functionality: - -```swift -import MijickCamera - -// Camera position -var cameraPosition: CameraPosition // .front or .back - -// Flash modes -var flashMode: CameraFlashMode // .off, .on, .auto -``` - -### Camera Features - -- Front/back camera switching -- Pinch-to-zoom -- Photo capture with quality settings -- HDR mode (premium) - - -## Ring Light System - -### How It Works - -The ring light is a colored overlay (`RingLightOverlay`) that surrounds the camera preview: - -- **Size**: Adjustable border width (10-120pt) -- **Color**: Preset colors or custom color picker -- **Opacity**: Adjustable brightness (10%-100%) -- **Toggle**: Can be enabled/disabled - -### Color Presets - -| Color | ID | Premium | -|-------|-----|---------| -| Pure White | `pureWhite` | No | -| Warm Cream | `warmCream` | No | -| Ice Blue | `iceBlue` | Yes | -| Soft Pink | `softPink` | Yes | -| Warm Amber | `warmAmber` | Yes | -| Cool Lavender | `coolLavender` | Yes | -| Custom | `custom` | Yes | - - -## Documentation Files - -When making changes, update the appropriate documentation: - -| File | Purpose | -|------|---------| -| `README.md` | User-facing app overview, setup instructions | -| `AI_Implementation.md` | Technical architecture, implementation details | -| `AGENTS.md` | Development guidelines (this file) | - -Always commit documentation updates with the related code changes. +- If installed, ensure SwiftLint returns no warnings or errors. +- Verify that documentation reflects any new functionality. +- Check for duplicate code before submitting. +- Ensure all new files follow the one-type-per-file rule. diff --git a/SelfieCam.xcodeproj/project.pbxproj b/SelfieCam.xcodeproj/project.pbxproj index 038987e..d06b271 100644 --- a/SelfieCam.xcodeproj/project.pbxproj +++ b/SelfieCam.xcodeproj/project.pbxproj @@ -34,6 +34,8 @@ EA836ABF2F0ACE8A00077F87 /* SelfieCam.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SelfieCam.app; sourceTree = BUILT_PRODUCTS_DIR; }; EA836ACC2F0ACE8B00077F87 /* SelfieCamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SelfieCamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; EA836AD62F0ACE8B00077F87 /* SelfieCamUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SelfieCamUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + EACONFIG002 /* SelfieCam/Configuration/Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = SelfieCam/Configuration/Debug.xcconfig; sourceTree = SOURCE_ROOT; }; + EACONFIG003 /* SelfieCam/Configuration/Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = SelfieCam/Configuration/Release.xcconfig; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -90,6 +92,7 @@ EA836ACF2F0ACE8B00077F87 /* SelfieCamTests */, EA836AD92F0ACE8B00077F87 /* SelfieCamUITests */, EA836AC02F0ACE8A00077F87 /* Products */, + EADCDD7D2F12FFC6007991B3 /* Recovered References */, ); sourceTree = ""; }; @@ -103,6 +106,15 @@ name = Products; sourceTree = ""; }; + EADCDD7D2F12FFC6007991B3 /* Recovered References */ = { + isa = PBXGroup; + children = ( + EACONFIG002 /* SelfieCam/Configuration/Debug.xcconfig */, + EACONFIG003 /* SelfieCam/Configuration/Release.xcconfig */, + ); + name = "Recovered References"; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -292,6 +304,7 @@ /* Begin XCBuildConfiguration section */ EA836ADE2F0ACE8B00077F87 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = EACONFIG002 /* SelfieCam/Configuration/Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -325,7 +338,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = 6R7KLBPBLZ; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -356,6 +369,7 @@ }; EA836ADF2F0ACE8B00077F87 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = EACONFIG003 /* SelfieCam/Configuration/Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -389,7 +403,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = 6R7KLBPBLZ; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -419,7 +433,7 @@ CODE_SIGN_ENTITLEMENTS = SelfieCam/SelfieCam.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 6R7KLBPBLZ; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to display your live selfie preview, apply real-time filters and ring light effects, capture high-quality photos, and enable advanced features like Center Stage auto-framing."; @@ -437,7 +451,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SelfieCam; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; @@ -457,7 +471,7 @@ CODE_SIGN_ENTITLEMENTS = SelfieCam/SelfieCam.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 6R7KLBPBLZ; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSCameraUsageDescription = "SelfieCam needs camera access to display your live selfie preview, apply real-time filters and ring light effects, capture high-quality photos, and enable advanced features like Center Stage auto-framing."; @@ -475,7 +489,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SelfieCam; + PRODUCT_BUNDLE_IDENTIFIER = "$(APP_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = YES; SWIFT_APPROACHABLE_CONCURRENCY = YES; @@ -493,11 +507,11 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 6R7KLBPBLZ; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 26.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SelfieCamTests; + PRODUCT_BUNDLE_IDENTIFIER = "$(TESTS_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; @@ -515,11 +529,11 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 6R7KLBPBLZ; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 26.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SelfieCamTests; + PRODUCT_BUNDLE_IDENTIFIER = "$(TESTS_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; @@ -536,10 +550,10 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 6R7KLBPBLZ; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SelfieCamUITests; + PRODUCT_BUNDLE_IDENTIFIER = "$(UITESTS_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; @@ -556,10 +570,10 @@ buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 6R7KLBPBLZ; + DEVELOPMENT_TEAM = "$(DEVELOPMENT_TEAM)"; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.SelfieCamUITests; + PRODUCT_BUNDLE_IDENTIFIER = "$(UITESTS_BUNDLE_IDENTIFIER)"; PRODUCT_NAME = "$(TARGET_NAME)"; STRING_CATALOG_GENERATE_SYMBOLS = NO; SWIFT_APPROACHABLE_CONCURRENCY = YES; diff --git a/SelfieCam/Configuration/Base.xcconfig b/SelfieCam/Configuration/Base.xcconfig new file mode 100644 index 0000000..2417a78 --- /dev/null +++ b/SelfieCam/Configuration/Base.xcconfig @@ -0,0 +1,18 @@ +// Base.xcconfig - Source of truth for all identifiers +// MIGRATION: Update COMPANY_IDENTIFIER and DEVELOPMENT_TEAM below + +// ============================================================================= +// COMPANY IDENTIFIER - CHANGE THIS FOR MIGRATION +// ============================================================================= + +COMPANY_IDENTIFIER = com.mbrucedogs +APP_NAME = SelfieCam +DEVELOPMENT_TEAM = 6R7KLBPBLZ + +// ============================================================================= +// DERIVED IDENTIFIERS - DO NOT EDIT +// ============================================================================= + +APP_BUNDLE_IDENTIFIER = $(COMPANY_IDENTIFIER).$(APP_NAME) +TESTS_BUNDLE_IDENTIFIER = $(COMPANY_IDENTIFIER).$(APP_NAME)Tests +UITESTS_BUNDLE_IDENTIFIER = $(COMPANY_IDENTIFIER).$(APP_NAME)UITests diff --git a/SelfieCam/Configuration/Debug.xcconfig b/SelfieCam/Configuration/Debug.xcconfig index 73ceabe..0689af4 100644 --- a/SelfieCam/Configuration/Debug.xcconfig +++ b/SelfieCam/Configuration/Debug.xcconfig @@ -1,6 +1,7 @@ // Debug.xcconfig // Configuration for Debug builds +#include "Base.xcconfig" #include? "Secrets.xcconfig" // If Secrets.xcconfig doesn't exist (CI/CD), fall back to empty values diff --git a/SelfieCam/Configuration/Release.xcconfig b/SelfieCam/Configuration/Release.xcconfig index 8d69132..d68603d 100644 --- a/SelfieCam/Configuration/Release.xcconfig +++ b/SelfieCam/Configuration/Release.xcconfig @@ -1,6 +1,7 @@ // Release.xcconfig // Configuration for Release builds +#include "Base.xcconfig" #include? "Secrets.xcconfig" // If Secrets.xcconfig doesn't exist (CI/CD), fall back to empty values diff --git a/SelfieCam/Resources/Localizable.xcstrings b/SelfieCam/Resources/Localizable.xcstrings index 9ab5698..f0eed5c 100644 --- a/SelfieCam/Resources/Localizable.xcstrings +++ b/SelfieCam/Resources/Localizable.xcstrings @@ -1,3330 +1,3367 @@ { - "sourceLanguage": "en", - "strings": { - "%@": { - "comment": "A button with an icon and label. The argument is the text to display in the button.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "%@" + "sourceLanguage" : "en", + "strings" : { + "%@" : { + "comment" : "A button with an icon and label. The argument is the text to display in the button.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "%@" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "%@" } } } }, - "%lld": { - "comment": "A text label displaying the currently selected ring light size. The text inside the label is replaced with the actual size value.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "%lld" : { + "comment" : "A text label displaying the currently selected ring light size. The text inside the label is replaced with the actual size value.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "%lld" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" } } } }, - "%lld percent": { - "comment": "The accessibility value of the ring light brightness slider, expressed as a percentage.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "%lld por ciento" + "%lld percent" : { + "comment" : "The accessibility value of the ring light brightness slider, expressed as a percentage.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld por ciento" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%lld pour cent" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld pour cent" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "%lld pour cent" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld pour cent" } } } }, - "%lld points": { - "comment": "The value of the ring size slider, displayed in parentheses.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "%lld puntos" + "%lld points" : { + "comment" : "The value of the ring size slider, displayed in parentheses.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld puntos" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%lld points" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld points" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "%lld points" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld points" } } } }, - "%lld%%": { - "comment": "A text label displaying the current brightness setting of the ring light, formatted as a percentage. The argument is the current brightness setting of the ring light, as a decimal between 0.0 and 1.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "%lld%%" + "%lld%%" : { + "comment" : "A text label displaying the current brightness setting of the ring light, formatted as a percentage. The argument is the current brightness setting of the ring light, as a decimal between 0.0 and 1.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld%%" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%lld%%" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld%%" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "%lld%%" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld%%" } } } }, - "%lldpt": { - "comment": "A label displaying the current ring size, formatted as a number followed by the unit \"pt\".", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "%lldpt" + "%lldpt" : { + "comment" : "A label displaying the current ring size, formatted as a number followed by the unit \"pt\".", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldpt" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "%lldpt" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldpt" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "%lldpt" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lldpt" } } } }, - "3s": { - "comment": "Display name for the \"3 seconds\" timer option.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "3s" + "3s" : { + "comment" : "Display name for the \"3 seconds\" timer option.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "3s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "3s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "3s" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "3s" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "3s" } } } }, - "5s": { - "comment": "Description of a timer option when the timer is set to 5 seconds.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "5s" + "5s" : { + "comment" : "Description of a timer option when the timer is set to 5 seconds.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "5s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "5s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "5s" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "5s" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "5s" } } } }, - "10%": { - "comment": "A label displayed alongside the left edge of the opacity slider.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "10%" + "10%" : { + "comment" : "A label displayed alongside the left edge of the opacity slider.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "10%" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "10%" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "10%" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "10%" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "10%" } } } }, - "10s": { - "comment": "Description of a timer option when the user selects \"10 seconds\".", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "10s" + "10s" : { + "comment" : "Description of a timer option when the user selects \"10 seconds\".", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "10s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "10s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "10s" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "10s" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "10s" } } } }, - "100%": { - "comment": "A label displayed alongside the right edge of the opacity slider.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "100%" + "100%" : { + "comment" : "A label displayed alongside the right edge of the opacity slider.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "100%" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "100%" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "100%" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "100%" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "100%" } } } }, - "Adjusts the brightness of the ring light": { - "comment": "A description of the ring light brightness slider.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Ajusta el brillo del aro de luz" + "Adjusts the brightness of the ring light" : { + "comment" : "A description of the ring light brightness slider.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajusta el brillo del aro de luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ajuste la luminosité de l'anneau lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajuste la luminosité de l'anneau lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Ajuste la luminosité de l'anneau lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajuste la luminosité de l'anneau lumineux" } } } }, - "Adjusts the size of the light ring around the camera preview": { - "comment": "A description of the ring size slider in the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Ajusta el tamaño del aro de luz alrededor de la vista previa" + "Adjusts the size of the light ring around the camera preview" : { + "comment" : "A description of the ring size slider in the settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajusta el tamaño del aro de luz alrededor de la vista previa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ajuste la taille de l'anneau lumineux autour de l'aperçu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajuste la taille de l'anneau lumineux autour de l'aperçu" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Ajuste la taille de l'anneau lumineux autour de l'aperçu" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajuste la taille de l'anneau lumineux autour de l'aperçu" } } } }, - "Applies light skin smoothing to the camera preview": { - "comment": "A hint for the \"Skin Smoothing\" toggle in the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Aplica suavizado ligero de piel a la vista previa" + "Applies light skin smoothing to the camera preview" : { + "comment" : "A hint for the \"Skin Smoothing\" toggle in the settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplica suavizado ligero de piel a la vista previa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Applique un lissage léger de la peau à l'aperçu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applique un lissage léger de la peau à l'aperçu" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Applique un lissage léger de la peau à l'aperçu" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applique un lissage léger de la peau à l'aperçu" } } } }, - "Applies subtle real-time skin smoothing": { - "comment": "Accessibility hint for the \"Skin Smoothing\" toggle in the Settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Aplica suavizado sutil de piel en tiempo real" + "Applies subtle real-time skin smoothing" : { + "comment" : "Accessibility hint for the \"Skin Smoothing\" toggle in the Settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aplica suavizado sutil de piel en tiempo real" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Applique un lissage subtil de la peau en temps réel" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applique un lissage subtil de la peau en temps réel" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Applique un lissage subtil de la peau en temps réel" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Applique un lissage subtil de la peau en temps réel" } } } }, - "Auto-Save": { - "comment": "Title of a toggle that enables automatic saving of captured photos and videos to the user's Photo Library.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Guardado Automático" + "Auto-Save" : { + "comment" : "Title of a toggle that enables automatic saving of captured photos and videos to the user's Photo Library.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardado Automático" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sauvegarde Auto" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sauvegarde Auto" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Sauvegarde Auto" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sauvegarde Auto" } } } }, - "Automatically adjusts camera to keep subject centered": { - "comment": "A hint that describes the functionality of the \"Enable Center Stage\" toggle.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Ajusta automáticamente la cámara para mantener al sujeto centrado" + "Automatically adjusts camera to keep subject centered" : { + "comment" : "A hint that describes the functionality of the \"Enable Center Stage\" toggle.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajusta automáticamente la cámara para mantener al sujeto centrado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ajuste automatiquement la caméra pour garder le sujet centré" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajuste automatiquement la caméra pour garder le sujet centré" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Ajuste automatiquement la caméra pour garder le sujet centré" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajuste automatiquement la caméra pour garder le sujet centré" } } } }, - "Automatically keeps you centered in the frame": { - "comment": "A description of the Center Stage feature.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Te mantiene centrado en el encuadre automáticamente" + "Automatically keeps you centered in the frame" : { + "comment" : "A description of the Center Stage feature.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Te mantiene centrado en el encuadre automáticamente" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vous garde automatiquement centré dans le cadre" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vous garde automatiquement centré dans le cadre" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Vous garde automatiquement centré dans le cadre" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vous garde automatiquement centré dans le cadre" } } } }, - "Automatically save captures to Photo Library": { - "comment": "A toggle option in the Settings view that allows the user to enable or disable automatic saving of captured photos and videos to the user's Photo Library.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Guardar capturas automáticamente en la Fototeca" + "Automatically save captures to Photo Library" : { + "comment" : "A toggle option in the Settings view that allows the user to enable or disable automatic saving of captured photos and videos to the user's Photo Library.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar capturas automáticamente en la Fototeca" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Enregistrer automatiquement les captures dans la Photothèque" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer automatiquement les captures dans la Photothèque" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Enregistrer automatiquement les captures dans la Photothèque" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer automatiquement les captures dans la Photothèque" } } } }, - "Back": { - "comment": "Option in the camera position picker for using the back camera.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Trasera" + "Back" : { + "comment" : "Option in the camera position picker for using the back camera.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trasera" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Arrière" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arrière" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Arrière" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Arrière" } } } }, - "Best Value • Save 33%": { - "comment": "A promotional text displayed below an annual subscription package, highlighting its value.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Mejor Valor • Ahorra 33%" + "Best Value • Save 33%" : { + "comment" : "A promotional text displayed below an annual subscription package, highlighting its value.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mejor Valor • Ahorra 33%" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Meilleure Valeur • Économisez 33%" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meilleure Valeur • Économisez 33%" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Meilleure Valeur • Économisez 33%" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meilleure Valeur • Économisez 33%" } } } }, - "Boomerang": { - "comment": "Display name for the \"Boomerang\" capture mode.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Boomerang" + "Boomerang" : { + "comment" : "Display name for the \"Boomerang\" capture mode.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boomerang" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Boomerang" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boomerang" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Boomerang" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Boomerang" } } } }, - "Camera": { - "comment": "Options for the camera position picker.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Cámara" + "Camera" : { + "comment" : "Options for the camera position picker.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cámara" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Caméra" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caméra" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Caméra" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Caméra" } } } }, - "Camera controls": { - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Controles de cámara" + "Camera controls" : { + "extractionState" : "stale", + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controles de cámara" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Contrôles de caméra" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôles de caméra" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Contrôles de caméra" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôles de caméra" } } } }, - "Camera Controls": { - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Controles de Cámara" + "Camera Controls" : { + "extractionState" : "stale", + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controles de Cámara" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Contrôles de Caméra" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôles de Caméra" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Contrôles de Caméra" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôles de Caméra" } } } }, - "Cancel": { - "comment": "The text for a button that dismisses the current view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Cancelar" + "Cancel" : { + "comment" : "The text for a button that dismisses the current view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Annuler" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Annuler" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler" } } } }, - "Captured photo": { - "comment": "A label describing a captured photo.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Foto capturada" + "Captured photo" : { + "comment" : "A label describing a captured photo.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foto capturada" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Photo capturée" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Photo capturée" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Photo capturée" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Photo capturée" } } } }, - "Captured video": { - "comment": "A label describing a captured video.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Video capturado" + "Captured video" : { + "comment" : "A label describing a captured video.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Video capturado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vidéo capturée" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vidéo capturée" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Vidéo capturée" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vidéo capturée" } } } }, - "Center Stage": { - "comment": "A label for the \"Center Stage\" button in the zoom control view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Encuadre Centrado" + "Center Stage" : { + "comment" : "A label for the \"Center Stage\" button in the zoom control view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encuadre Centrado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Cadrage Centré" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cadrage Centré" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Cadrage Centré" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cadrage Centré" } } } }, - "Center Stage active": { - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Encuadre Centrado activo" + "Center Stage active" : { + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encuadre Centrado activo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Cadrage Centré actif" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cadrage Centré actif" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Cadrage Centré actif" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cadrage Centré actif" } } } }, - "Center Stage Auto-Framing": { - "comment": "Benefit of the \"Go Pro\" premium package: Automatic centering of the subject in the photo.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Encuadre Automático Center Stage" + "Center Stage Auto-Framing" : { + "comment" : "Benefit of the \"Go Pro\" premium package: Automatic centering of the subject in the photo.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Encuadre Automático Center Stage" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Cadrage Automatique Center Stage" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cadrage Automatique Center Stage" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Cadrage Automatique Center Stage" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cadrage Automatique Center Stage" } } } }, - "Choose between front and back camera lenses": { - "comment": "A description of the camera position picker.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Elige entre cámara frontal y trasera" + "Choose between front and back camera lenses" : { + "comment" : "A description of the camera position picker.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elige entre cámara frontal y trasera" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Choisissez entre caméra frontale et arrière" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choisissez entre caméra frontale et arrière" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Choisissez entre caméra frontale et arrière" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choisissez entre caméra frontale et arrière" } } } }, - "Choose the color of the ring light around the camera preview": { - "comment": "A description under the title of the light color preset section, explaining its purpose.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Elige el color del aro de luz alrededor de la vista previa de la cámara" + "Choose the color of the ring light around the camera preview" : { + "comment" : "A description under the title of the light color preset section, explaining its purpose.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elige el color del aro de luz alrededor de la vista previa de la cámara" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Choisissez la couleur de l'anneau lumineux autour de l'aperçu de la caméra" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choisissez la couleur de l'anneau lumineux autour de l'aperçu de la caméra" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Choisissez la couleur de l'anneau lumineux autour de l'aperçu de la caméra" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choisissez la couleur de l'anneau lumineux autour de l'aperçu de la caméra" } } } }, - "Close": { - "comment": "A button label that closes the view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Cerrar" + "Close" : { + "comment" : "A button label that closes the view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrar" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Fermer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Fermer" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer" } } } }, - "Close preview": { - "comment": "A button label that closes the preview screen.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Cerrar vista previa" + "Close preview" : { + "comment" : "A button label that closes the preview screen.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cerrar vista previa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Fermer l'aperçu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer l'aperçu" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Fermer l'aperçu" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fermer l'aperçu" } } } }, - "Controls automatic flash behavior for photos": { - "comment": "A description below the flash mode picker, explaining its purpose.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Controla el comportamiento automático del flash para fotos" + "Controls automatic flash behavior for photos" : { + "comment" : "A description below the flash mode picker, explaining its purpose.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Controla el comportamiento automático del flash para fotos" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Contrôle le comportement automatique du flash pour les photos" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôle le comportement automatique du flash pour les photos" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Contrôle le comportement automatique du flash pour les photos" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Contrôle le comportement automatique du flash pour les photos" } } } }, - "Cool Lavender": { - "comment": "Name of a ring light color preset.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Lavanda Fresco" + "Cool Lavender" : { + "comment" : "Name of a ring light color preset.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lavanda Fresco" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lavande Fraîche" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lavande Fraîche" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Lavande Fraîche" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lavande Fraîche" } } } }, - "Custom": { - "comment": "A label displayed below the rainbow gradient circle in the custom color button.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Personalizado" + "Custom" : { + "comment" : "A label displayed below the rainbow gradient circle in the custom color button.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personalizado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Personnalisé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personnalisé" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Personnalisé" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personnalisé" } } } }, - "Custom color": { - "comment": "An accessibility label for the custom color button.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Color personalizado" + "Custom color" : { + "comment" : "An accessibility label for the custom color button.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color personalizado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Couleur personnalisée" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur personnalisée" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Couleur personnalisée" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur personnalisée" } } } }, - "Debug mode: Purchase simulated!": { - "comment": "Announcement posted to VoiceOver when a premium purchase is simulated in debug mode.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Modo de depuración: ¡Compra simulada!" + "Debug mode: Purchase simulated!" : { + "comment" : "Announcement posted to VoiceOver when a premium purchase is simulated in debug mode.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de depuración: ¡Compra simulada!" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode débogage : Achat simulé !" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode débogage : Achat simulé !" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Mode débogage : Achat simulé !" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode débogage : Achat simulé !" } } } }, - "Debug mode: Restore simulated!": { - "comment": "Accessibility announcement when restoring purchases in debug mode.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Modo de depuración: ¡Restauración simulada!" + "Debug mode: Restore simulated!" : { + "comment" : "Accessibility announcement when restoring purchases in debug mode.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de depuración: ¡Restauración simulada!" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode débogage : Restauration simulée !" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode débogage : Restauration simulée !" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Mode débogage : Restauration simulée !" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode débogage : Restauration simulée !" } } } }, - "Delay before photo capture for self-portraits": { - "comment": "A description of the purpose of the \"Self-Timer\" setting in the settings screen.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Retraso antes de capturar para autorretratos" + "Delay before photo capture for self-portraits" : { + "comment" : "A description of the purpose of the \"Self-Timer\" setting in the settings screen.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retraso antes de capturar para autorretratos" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Délai avant la capture pour les autoportraits" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Délai avant la capture pour les autoportraits" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Délai avant la capture pour les autoportraits" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Délai avant la capture pour les autoportraits" } } } }, - "Done": { - "comment": "The text for a button that dismisses a view. In this case, it dismisses the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Listo" + "Done" : { + "comment" : "The text for a button that dismisses a view. In this case, it dismisses the settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Listo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Terminé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminé" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Terminé" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminé" } } } }, - "Double tap to capture a photo": { - "comment": "An accessibility hint for the capture button, instructing the user to double-tap it to capture a photo.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Toca dos veces para capturar una foto" + "Double tap to capture a photo" : { + "comment" : "An accessibility hint for the capture button, instructing the user to double-tap it to capture a photo.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toca dos veces para capturar una foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Appuyez deux fois pour capturer une photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appuyez deux fois pour capturer une photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Appuyez deux fois pour capturer une photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appuyez deux fois pour capturer une photo" } } } }, - "Enable Center Stage": { - "comment": "An accessibility label for the toggle that enables the \"Center Stage\" feature.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Activar Encuadre Centrado" + "Enable Center Stage" : { + "comment" : "An accessibility label for the toggle that enables the \"Center Stage\" feature.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar Encuadre Centrado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer Cadrage Centré" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer Cadrage Centré" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Activer Cadrage Centré" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer Cadrage Centré" } } } }, - "Enable Ring Light": { - "comment": "Title of a toggle in the Settings view that allows the user to enable or disable the ring light overlay.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Activar Aro de Luz" + "Enable Ring Light" : { + "comment" : "Title of a toggle in the Settings view that allows the user to enable or disable the ring light overlay.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activar Aro de Luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activer Anneau Lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer Anneau Lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Activer Anneau Lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activer Anneau Lumineux" } } } }, - "Enables or disables the ring light overlay": { - "comment": "A toggle that enables or disables the ring light overlay.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Activa o desactiva el aro de luz" + "Enables or disables the ring light overlay" : { + "comment" : "A toggle that enables or disables the ring light overlay.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activa o desactiva el aro de luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Active ou désactive l'anneau lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Active ou désactive l'anneau lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Active ou désactive l'anneau lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Active ou désactive l'anneau lumineux" } } } }, - "Extended Self-Timers (5s, 10s)": { - "comment": "Benefit description for the extended self-timers option.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Temporizadores Extendidos (5s, 10s)" + "Extended Self-Timers (5s, 10s)" : { + "comment" : "Benefit description for the extended self-timers option.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temporizadores Extendidos (5s, 10s)" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Retardateurs Étendus (5s, 10s)" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retardateurs Étendus (5s, 10s)" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Retardateurs Étendus (5s, 10s)" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retardateurs Étendus (5s, 10s)" } } } }, - "File size and image quality for saved photos": { - "comment": "A description of the photo quality setting.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Tamaño de archivo y calidad de imagen para fotos guardadas" + "File size and image quality for saved photos" : { + "comment" : "A description of the photo quality setting.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño de archivo y calidad de imagen para fotos guardadas" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille de fichier et qualité d'image pour les photos enregistrées" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de fichier et qualité d'image pour les photos enregistrées" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Taille de fichier et qualité d'image pour les photos enregistrées" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de fichier et qualité d'image pour les photos enregistrées" } } } }, - "Flash Mode": { - "comment": "Title of a segmented picker that allows the user to select the flash mode of the camera.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Modo de Flash" + "Flash Mode" : { + "comment" : "Title of a segmented picker that allows the user to select the flash mode of the camera.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo de Flash" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode Flash" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode Flash" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Mode Flash" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode Flash" } } } }, - "Flash Sync": { - "comment": "Title of a toggle that synchronizes the flash color with the ring light color.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincronizar Flash" + "Flash Sync" : { + "comment" : "Title of a toggle that synchronizes the flash color with the ring light color.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincronizar Flash" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchroniser Flash" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchroniser Flash" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchroniser Flash" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchroniser Flash" } } } }, - "Flash Sync with Ring Light": { - "comment": "Benefit description for the \"Flash Sync with Ring Light\" feature.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincronización de Flash con Aro de Luz" + "Flash Sync with Ring Light" : { + "comment" : "Benefit description for the \"Flash Sync with Ring Light\" feature.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincronización de Flash con Aro de Luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchronisation du Flash avec l'Anneau Lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisation du Flash avec l'Anneau Lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchronisation du Flash avec l'Anneau Lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisation du Flash avec l'Anneau Lumineux" } } } }, - "Flips the camera preview horizontally": { - "comment": "An accessibility hint for the \"True Mirror\" setting.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Voltea la vista previa horizontalmente" + "Flips the camera preview horizontally" : { + "comment" : "An accessibility hint for the \"True Mirror\" setting.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voltea la vista previa horizontalmente" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Retourne l'aperçu horizontalement" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retourne l'aperçu horizontalement" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Retourne l'aperçu horizontalement" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retourne l'aperçu horizontalement" } } } }, - "Front": { - "comment": "Option in the camera position picker for using the front camera.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Frontal" + "Front" : { + "comment" : "Option in the camera position picker for using the front camera.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Frontal" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Frontale" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Frontale" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Frontale" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Frontale" } } } }, - "Front Flash": { - "comment": "Title of a toggle in the Settings view that controls whether the front flash is enabled.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Flash Frontal" + "Front Flash" : { + "comment" : "Title of a toggle in the Settings view that controls whether the front flash is enabled.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flash Frontal" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Flash Frontal" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flash Frontal" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Flash Frontal" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flash Frontal" } } } }, - "Get ready!": { - "comment": "A text displayed in the countdown overlay when a photo is about to be taken.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "¡Prepárate!" + "Get ready!" : { + "comment" : "A text displayed in the countdown overlay when a photo is about to be taken.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Prepárate!" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Préparez-vous !" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Préparez-vous !" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Préparez-vous !" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Préparez-vous !" } } } }, - "Go Pro": { - "comment": "The title of the \"Go Pro\" button in the Pro paywall.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Hazte Pro" + "Go Pro" : { + "comment" : "The title of the \"Go Pro\" button in the Pro paywall.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hazte Pro" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Passer Pro" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passer Pro" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Passer Pro" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passer Pro" } } } }, - "Grid Overlay": { - "comment": "Text displayed in a settings toggle for showing a grid overlay to help compose your shot.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Cuadrícula" + "Grid Overlay" : { + "comment" : "Text displayed in a settings toggle for showing a grid overlay to help compose your shot.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuadrícula" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Grille" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grille" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Grille" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Grille" } } } }, - "HDR Mode": { - "comment": "Title for a picker that allows the user to select the HDR mode of the camera.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Modo HDR" + "HDR Mode" : { + "comment" : "Title for a picker that allows the user to select the HDR mode of the camera.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo HDR" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode HDR" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode HDR" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Mode HDR" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode HDR" } } } }, - "HDR Mode for Better Photos": { - "comment": "Benefit description for the \"HDR Mode for Better Photos\" benefit.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Modo HDR para Mejores Fotos" + "HDR Mode for Better Photos" : { + "comment" : "Benefit description for the \"HDR Mode for Better Photos\" benefit.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo HDR para Mejores Fotos" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode HDR pour de Meilleures Photos" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode HDR pour de Meilleures Photos" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Mode HDR pour de Meilleures Photos" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode HDR pour de Meilleures Photos" } } } }, - "Hide preview during capture for flash effect": { - "comment": "Text displayed in a toggle within the \"Camera Controls\" section, allowing the user to enable or disable the feature of hiding the camera preview during a photo capture to simulate a flash effect.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Ocultar vista previa durante captura para efecto de flash" + "Hide preview during capture for flash effect" : { + "comment" : "Text displayed in a toggle within the \"Camera Controls\" section, allowing the user to enable or disable the feature of hiding the camera preview during a photo capture to simulate a flash effect.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocultar vista previa durante captura para efecto de flash" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Masquer l'aperçu pendant la capture pour l'effet flash" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masquer l'aperçu pendant la capture pour l'effet flash" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Masquer l'aperçu pendant la capture pour l'effet flash" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masquer l'aperçu pendant la capture pour l'effet flash" } } } }, - "High Dynamic Range for better lighting in photos": { - "comment": "A description of the High Dynamic Range (HDR) mode in the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Alto Rango Dinámico para mejor iluminación en fotos" + "High Dynamic Range for better lighting in photos" : { + "comment" : "A description of the High Dynamic Range (HDR) mode in the settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alto Rango Dinámico para mejor iluminación en fotos" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Haute Gamme Dynamique pour un meilleur éclairage des photos" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haute Gamme Dynamique pour un meilleur éclairage des photos" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Haute Gamme Dynamique pour un meilleur éclairage des photos" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haute Gamme Dynamique pour un meilleur éclairage des photos" } } } }, - "High Quality Photo Export": { - "comment": "Description of a benefit that is included with the Premium membership.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Exportación de Fotos en Alta Calidad" + "High Quality Photo Export" : { + "comment" : "Description of a benefit that is included with the Premium membership.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportación de Fotos en Alta Calidad" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Exportation de Photos en Haute Qualité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportation de Photos en Haute Qualité" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Exportation de Photos en Haute Qualité" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Exportation de Photos en Haute Qualité" } } } }, - "Ice Blue": { - "comment": "Name of a ring light color preset.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Azul Hielo" + "Ice Blue" : { + "comment" : "Name of a ring light color preset.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azul Hielo" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Bleu Glacé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bleu Glacé" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Bleu Glacé" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bleu Glacé" } } } }, - "iCloud Sync": { - "comment": "Title of the section that allows users to enable or disable iCloud sync of their photos and videos.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincronización iCloud" + "iCloud Sync" : { + "comment" : "Title of the section that allows users to enable or disable iCloud sync of their photos and videos.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincronización iCloud" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchronisation iCloud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisation iCloud" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchronisation iCloud" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisation iCloud" } } } }, - "Last synced %@": { - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Última sincronización %@" + "Last synced %@" : { + "extractionState" : "stale", + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Última sincronización %@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Dernière synchronisation %@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dernière synchronisation %@" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Dernière synchronisation %@" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dernière synchronisation %@" } } } }, - "Light Color": { - "comment": "A label displayed above a section of the settings view related to light colors.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Color de Luz" + "Light Color" : { + "comment" : "A label displayed above a section of the settings view related to light colors.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color de Luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Couleur de Lumière" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur de Lumière" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Couleur de Lumière" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur de Lumière" } } } }, - "Locked. Tap to unlock with Pro.": { - "comment": "A hint that appears when a user taps on a color preset button.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Bloqueado. Toca para desbloquear con Pro." + "Locked. Tap to unlock with Pro." : { + "comment" : "A hint that appears when a user taps on a color preset button.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloqueado. Toca para desbloquear con Pro." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Verrouillé. Appuyez pour déverrouiller avec Pro." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verrouillé. Appuyez pour déverrouiller avec Pro." } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Verrouillé. Appuyez pour déverrouiller avec Pro." + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verrouillé. Appuyez pour déverrouiller avec Pro." } } } }, - "Off": { - "comment": "The accessibility value for the grid toggle when it is off.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Desactivado" + "Off" : { + "comment" : "The accessibility value for the grid toggle when it is off.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Desactivado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Désactivé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactivé" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Désactivé" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Désactivé" } } } }, - "On": { - "comment": "A value that describes a control item as \"On\".", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Activado" + "On" : { + "comment" : "A value that describes a control item as \"On\".", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Activé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activé" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Activé" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Activé" } } } }, - "Open Source Licenses": { - "comment": "A heading displayed above a list of open source licenses used in the app.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Licencias de Código Abierto" + "Open Source Licenses" : { + "comment" : "A heading displayed above a list of open source licenses used in the app.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Licencias de Código Abierto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Licences Open Source" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Licences Open Source" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Licences Open Source" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Licences Open Source" } } } }, - "Opens upgrade options": { - "comment": "An accessibility hint for the \"Upgrade to Pro\" button that indicates it opens upgrade options.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Abre opciones de mejora" + "Opens upgrade options" : { + "comment" : "An accessibility hint for the \"Upgrade to Pro\" button that indicates it opens upgrade options.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Abre opciones de mejora" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ouvre les options de mise à niveau" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvre les options de mise à niveau" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Ouvre les options de mise à niveau" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ouvre les options de mise à niveau" } } } }, - "Photo": { - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Foto" + "Photo" : { + "extractionState" : "stale", + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Photo" } } } }, - "Photo Quality": { - "comment": "Title of a segmented picker that allows the user to select the photo quality.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Calidad de Foto" + "Photo Quality" : { + "comment" : "Title of a segmented picker that allows the user to select the photo quality.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Calidad de Foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Qualité Photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Qualité Photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Qualité Photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Qualité Photo" } } } }, - "Photo review": { - "comment": "The title of the view that lets users review and save or share a photo.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Revisar foto" + "Photo review" : { + "comment" : "The title of the view that lets users review and save or share a photo.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Revisar foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Aperçu photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aperçu photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Aperçu photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aperçu photo" } } } }, - "Premium color": { - "comment": "An accessibility hint for a premium color option in the color preset button.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Color premium" + "Premium color" : { + "comment" : "An accessibility hint for a premium color option in the color preset button.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color premium" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Couleur premium" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur premium" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Couleur premium" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur premium" } } } }, - "Premium Colors + Custom Color Picker": { - "comment": "Benefit description for the \"Premium Colors + Custom Color Picker\" benefit.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Colores Premium + Selector de Color Personalizado" + "Premium Colors + Custom Color Picker" : { + "comment" : "Benefit description for the \"Premium Colors + Custom Color Picker\" benefit.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Colores Premium + Selector de Color Personalizado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Couleurs Premium + Sélecteur de Couleur Personnalisé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleurs Premium + Sélecteur de Couleur Personnalisé" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Couleurs Premium + Sélecteur de Couleur Personnalisé" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleurs Premium + Sélecteur de Couleur Personnalisé" } } } }, - "Premium colors, HDR, timers & more": { - "comment": "A description of the additional features available in the Pro version of the app.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Colores premium, HDR, temporizadores y más" + "Premium colors, HDR, timers & more" : { + "comment" : "A description of the additional features available in the Pro version of the app.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Colores premium, HDR, temporizadores y más" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Couleurs premium, HDR, retardateurs et plus" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleurs premium, HDR, retardateurs et plus" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Couleurs premium, HDR, retardateurs et plus" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleurs premium, HDR, retardateurs et plus" } } } }, - "Purchase successful! Pro features unlocked.": { - "comment": "Announcement read out to the user when a premium purchase is successful.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "¡Compra exitosa! Funciones Pro desbloqueadas." + "Purchase successful! Pro features unlocked." : { + "comment" : "Announcement read out to the user when a premium purchase is successful.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Compra exitosa! Funciones Pro desbloqueadas." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Achat réussi ! Fonctionnalités Pro déverrouillées." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Achat réussi ! Fonctionnalités Pro déverrouillées." } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Achat réussi ! Fonctionnalités Pro déverrouillées." + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Achat réussi ! Fonctionnalités Pro déverrouillées." } } } }, - "Purchases restored": { - "comment": "Announcement read out to the user when purchases are restored.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Compras restauradas" + "Purchases restored" : { + "comment" : "Announcement read out to the user when purchases are restored.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compras restauradas" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Achats restaurés" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Achats restaurés" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Achats restaurés" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Achats restaurés" } } } }, - "Pure White": { - "comment": "A color preset option for the ring light that displays as pure white.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Blanco Puro" + "Pure White" : { + "comment" : "A color preset option for the ring light that displays as pure white.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanco Puro" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Blanc Pur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanc Pur" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Blanc Pur" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blanc Pur" } } } }, - "Restore Purchases": { - "comment": "A button that restores purchases.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Restaurar Compras" + "Restore Purchases" : { + "comment" : "A button that restores purchases.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar Compras" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Restaurer les Achats" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurer les Achats" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Restaurer les Achats" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurer les Achats" } } } }, - "Retake": { - "comment": "Title for a button that allows the user to retake a captured photo or video.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Repetir" + "Retake" : { + "comment" : "Title for a button that allows the user to retake a captured photo or video.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repetir" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Reprendre" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reprendre" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Reprendre" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reprendre" } } } }, - "Retake photo": { - "comment": "A button that, when tapped, allows the user to retake a photo.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Tomar otra foto" + "Retake photo" : { + "comment" : "A button that, when tapped, allows the user to retake a photo.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tomar otra foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Reprendre la photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reprendre la photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Reprendre la photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reprendre la photo" } } } }, - "Ring Light": { - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Aro de Luz" + "Ring Light" : { + "extractionState" : "stale", + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aro de Luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Anneau Lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anneau Lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Anneau Lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anneau Lumineux" } } } }, - "Ring light brightness": { - "comment": "An accessibility label for the ring light brightness setting in the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Brillo del aro de luz" + "Ring light brightness" : { + "comment" : "An accessibility label for the ring light brightness setting in the settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brillo del aro de luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Luminosité de l'anneau lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luminosité de l'anneau lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Luminosité de l'anneau lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luminosité de l'anneau lumineux" } } } }, - "Ring Light Brightness": { - "comment": "The title of the overlay that appears when the user taps the ring light button.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Brillo del Aro de Luz" + "Ring Light Brightness" : { + "comment" : "The title of the overlay that appears when the user taps the ring light button.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brillo del Aro de Luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Luminosité de l'Anneau Lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luminosité de l'Anneau Lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Luminosité de l'Anneau Lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Luminosité de l'Anneau Lumineux" } } } }, - "Ring Light Color": { - "comment": "The title of the color picker overlay.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Color del Aro de Luz" + "Ring Light Color" : { + "comment" : "The title of the color picker overlay.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Color del Aro de Luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Couleur de l'Anneau Lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur de l'Anneau Lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Couleur de l'Anneau Lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Couleur de l'Anneau Lumineux" } } } }, - "Ring Light Size": { - "comment": "The title of the slider that allows the user to select the size of their ring light.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Tamaño del Aro de Luz" + "Ring Light Size" : { + "comment" : "The title of the slider that allows the user to select the size of their ring light.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño del Aro de Luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille de l'Anneau Lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de l'Anneau Lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Taille de l'Anneau Lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de l'Anneau Lumineux" } } } }, - "Ring size": { - "comment": "An accessibility label for the ring size slider in the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Tamaño del aro" + "Ring size" : { + "comment" : "An accessibility label for the ring size slider in the settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño del aro" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille de l'anneau" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de l'anneau" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Taille de l'anneau" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de l'anneau" } } } }, - "Ring Size": { - "comment": "The label for the ring size slider in the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Tamaño del Aro" + "Ring Size" : { + "comment" : "The label for the ring size slider in the settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tamaño del Aro" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Taille de l'Anneau" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de l'Anneau" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Taille de l'Anneau" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Taille de l'Anneau" } } } }, - "Save": { - "comment": "Title for a button that saves the currently captured photo or video to the user's photo library.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Guardar" + "Save" : { + "comment" : "Title for a button that saves the currently captured photo or video to the user's photo library.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Enregistrer" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Enregistrer" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer" } } } }, - "Save photo": { - "comment": "A button that saves the currently displayed photo to the user's library.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Guardar foto" + "Save photo" : { + "comment" : "A button that saves the currently displayed photo to the user's library.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardar foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Enregistrer la photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer la photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Enregistrer la photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrer la photo" } } } }, - "Saved to Photos": { - "comment": "Text shown as a toast message when a photo is successfully saved to Photos.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Guardado en Fotos" + "Saved to Photos" : { + "comment" : "Text shown as a toast message when a photo is successfully saved to Photos.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardado en Fotos" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Enregistré dans Photos" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistré dans Photos" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Enregistré dans Photos" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistré dans Photos" } } } }, - "Saves the photo to your library": { - "comment": "An accessibility hint for the save button in the photo review view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Guarda la foto en tu biblioteca" + "Saves the photo to your library" : { + "comment" : "An accessibility hint for the save button in the photo review view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guarda la foto en tu biblioteca" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Enregistre la photo dans votre photothèque" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistre la photo dans votre photothèque" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Enregistre la photo dans votre photothèque" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistre la photo dans votre photothèque" } } } }, - "Saving...": { - "comment": "A text that appears while a photo is being saved.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Guardando..." + "Saving..." : { + "comment" : "A text that appears while a photo is being saved.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Guardando..." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Enregistrement..." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrement..." } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Enregistrement..." + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Enregistrement..." } } } }, - "Select camera position": { - "comment": "A label describing the action of selecting a camera position.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar posición de cámara" + "Select camera position" : { + "comment" : "A label describing the action of selecting a camera position.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar posición de cámara" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner la position de la caméra" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner la position de la caméra" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner la position de la caméra" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner la position de la caméra" } } } }, - "Select flash mode": { - "comment": "An accessibility label for the flash mode picker in the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar modo de flash" + "Select flash mode" : { + "comment" : "An accessibility label for the flash mode picker in the settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar modo de flash" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner le mode flash" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner le mode flash" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner le mode flash" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner le mode flash" } } } }, - "Select HDR mode": { - "comment": "A label describing the action of selecting an HDR mode in the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar modo HDR" + "Select HDR mode" : { + "comment" : "A label describing the action of selecting an HDR mode in the settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar modo HDR" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner le mode HDR" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner le mode HDR" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner le mode HDR" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner le mode HDR" } } } }, - "Select photo quality": { - "comment": "A label describing a segmented picker for selecting photo quality.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar calidad de foto" + "Select photo quality" : { + "comment" : "A label describing a segmented picker for selecting photo quality.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar calidad de foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner la qualité photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner la qualité photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner la qualité photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner la qualité photo" } } } }, - "Select self-timer duration": { - "comment": "A label describing the segmented control for selecting the duration of the self-timer.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Seleccionar duración del temporizador" + "Select self-timer duration" : { + "comment" : "A label describing the segmented control for selecting the duration of the self-timer.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Seleccionar duración del temporizador" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner la durée du retardateur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner la durée du retardateur" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Sélectionner la durée du retardateur" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sélectionner la durée du retardateur" } } } }, - "Self-Timer": { - "comment": "Title of the section in the settings view that allows the user to select the duration of the self-timer.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Temporizador" + "Self-Timer" : { + "comment" : "Title of the section in the settings view that allows the user to select the duration of the self-timer.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Temporizador" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Retardateur" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retardateur" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Retardateur" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retardateur" } } } }, - "Settings": { - "comment": "The title of the settings screen.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Configuración" + "Settings" : { + "comment" : "The title of the settings screen.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Réglages" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réglages" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Réglages" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réglages" } } } }, - "Share": { - "comment": "Title for a button that shares the captured media.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Compartir" + "Share" : { + "comment" : "Title for a button that shares the captured media.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Partager" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Partager" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager" } } } }, - "Share photo": { - "comment": "An accessibility label for the share button.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Compartir foto" + "Share photo" : { + "comment" : "An accessibility label for the share button.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Partager la photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager la photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Partager la photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager la photo" } } } }, - "Show colored light ring around camera preview": { - "comment": "Subtitle for the \"Enable Ring Light\" toggle in the Settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Mostrar aro de luz de color alrededor de la vista previa" + "Show colored light ring around camera preview" : { + "comment" : "Subtitle for the \"Enable Ring Light\" toggle in the Settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mostrar aro de luz de color alrededor de la vista previa" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Afficher l'anneau lumineux coloré autour de l'aperçu" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher l'anneau lumineux coloré autour de l'aperçu" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Afficher l'anneau lumineux coloré autour de l'aperçu" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afficher l'anneau lumineux coloré autour de l'aperçu" } } } }, - "Shows a grid overlay to help compose your shot": { - "comment": "A toggle that enables or disables the rule of thirds grid overlay in the camera view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Muestra una cuadrícula para ayudar a componer tu foto" + "Shows a grid overlay to help compose your shot" : { + "comment" : "A toggle that enables or disables the rule of thirds grid overlay in the camera view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muestra una cuadrícula para ayudar a componer tu foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Affiche une grille pour vous aider à composer votre photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Affiche une grille pour vous aider à composer votre photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Affiche une grille pour vous aider à composer votre photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Affiche une grille pour vous aider à composer votre photo" } } } }, - "Shows horizontally flipped preview like a real mirror": { - "comment": "Description of a setting that flips the camera preview horizontally.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Muestra vista previa volteada horizontalmente como un espejo real" + "Shows horizontally flipped preview like a real mirror" : { + "comment" : "Description of a setting that flips the camera preview horizontally.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muestra vista previa volteada horizontalmente como un espejo real" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Affiche un aperçu inversé horizontalement comme un vrai miroir" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Affiche un aperçu inversé horizontalement comme un vrai miroir" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Affiche un aperçu inversé horizontalement comme un vrai miroir" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Affiche un aperçu inversé horizontalement comme un vrai miroir" } } } }, - "Shows rule of thirds grid for composition": { - "comment": "A toggle that enables or disables the display of a rule of thirds grid on the camera preview.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Muestra cuadrícula de tercios para composición" + "Shows rule of thirds grid for composition" : { + "comment" : "A toggle that enables or disables the display of a rule of thirds grid on the camera preview.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Muestra cuadrícula de tercios para composición" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Affiche une grille de règle des tiers pour la composition" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Affiche une grille de règle des tiers pour la composition" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Affiche une grille de règle des tiers pour la composition" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Affiche une grille de règle des tiers pour la composition" } } } }, - "Sign in to iCloud to enable sync": { - "comment": "Subtitle of the iCloud sync section when the user is not signed into iCloud.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Inicia sesión en iCloud para activar la sincronización" + "Sign in to iCloud to enable sync" : { + "comment" : "Subtitle of the iCloud sync section when the user is not signed into iCloud.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inicia sesión en iCloud para activar la sincronización" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Connectez-vous à iCloud pour activer la synchronisation" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connectez-vous à iCloud pour activer la synchronisation" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Connectez-vous à iCloud pour activer la synchronisation" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Connectez-vous à iCloud pour activer la synchronisation" } } } }, - "Skin Smoothing": { - "comment": "A toggle that enables or disables real-time skin smoothing.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Suavizado de Piel" + "Skin Smoothing" : { + "comment" : "A toggle that enables or disables real-time skin smoothing.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suavizado de Piel" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lissage de Peau" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lissage de Peau" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Lissage de Peau" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lissage de Peau" } } } }, - "Skin Smoothing Beauty Filter": { - "comment": "Text for a benefit row in the ProPaywallView, describing a feature that is included with the Premium membership.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Filtro de Belleza con Suavizado de Piel" + "Skin Smoothing Beauty Filter" : { + "comment" : "Text for a benefit row in the ProPaywallView, describing a feature that is included with the Premium membership.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtro de Belleza con Suavizado de Piel" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Filtre Beauté avec Lissage de Peau" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtre Beauté avec Lissage de Peau" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Filtre Beauté avec Lissage de Peau" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Filtre Beauté avec Lissage de Peau" } } } }, - "Soft Pink": { - "comment": "Name of a ring light color preset.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Rosa Suave" + "Soft Pink" : { + "comment" : "Name of a ring light color preset.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rosa Suave" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Rose Doux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rose Doux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Rose Doux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rose Doux" } } } }, - "Subscribe to %@ for %@": { - "comment": "A button that triggers a purchase of a premium content package. The label text is generated based on the package's title and price.", - "isCommentAutoGenerated": true, - "localizations": { - "en": { - "stringUnit": { - "state": "translated", - "value": "Subscribe to %1$@ for %2$@" + "Subscribe to %@ for %@" : { + "comment" : "A button that triggers a purchase of a premium content package. The label text is generated based on the package's title and price.", + "isCommentAutoGenerated" : true, + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Subscribe to %1$@ for %2$@" } }, - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Suscribirse a %1$@ por %2$@" + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suscribirse a %1$@ por %2$@" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "S'abonner à %1$@ pour %2$@" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "S'abonner à %1$@ pour %2$@" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "S'abonner à %1$@ pour %2$@" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "S'abonner à %1$@ pour %2$@" } } } }, - "Sync Now": { - "comment": "A button label that triggers a sync action.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincronizar Ahora" + "Sync Now" : { + "comment" : "A button label that triggers a sync action.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincronizar Ahora" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchroniser Maintenant" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchroniser Maintenant" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchroniser Maintenant" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchroniser Maintenant" } } } }, - "Sync Settings": { - "comment": "Title of a toggle that allows the user to enable or disable iCloud sync settings.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincronizar Configuración" + "Sync Settings" : { + "comment" : "Title of a toggle that allows the user to enable or disable iCloud sync settings.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincronizar Configuración" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchroniser les Réglages" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchroniser les Réglages" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchroniser les Réglages" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchroniser les Réglages" } } } }, - "Sync settings across all your devices": { - "comment": "Subtitle of the \"Sync Settings\" toggle in the Settings view, describing the functionality when sync is enabled.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincroniza la configuración en todos tus dispositivos" + "Sync settings across all your devices" : { + "comment" : "Subtitle of the \"Sync Settings\" toggle in the Settings view, describing the functionality when sync is enabled.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincroniza la configuración en todos tus dispositivos" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchronisez les réglages sur tous vos appareils" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisez les réglages sur tous vos appareils" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchronisez les réglages sur tous vos appareils" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisez les réglages sur tous vos appareils" } } } }, - "Synced": { - "comment": "Text displayed in the iCloud sync section when the user's settings have been successfully synced.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincronizado" + "Synced" : { + "comment" : "Text displayed in the iCloud sync section when the user's settings have been successfully synced.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincronizado" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchronisé" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisé" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchronisé" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisé" } } } }, - "Syncing...": { - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincronizando..." + "Syncing..." : { + "extractionState" : "stale", + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincronizando..." } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchronisation..." + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisation..." } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchronisation..." + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronisation..." } } } }, - "Syncs flash color with ring light color": { - "comment": "A toggle that synchronizes the flash color with the ring light color.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincroniza el color del flash con el color del aro de luz" + "Syncs flash color with ring light color" : { + "comment" : "A toggle that synchronizes the flash color with the ring light color.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincroniza el color del flash con el color del aro de luz" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchronise la couleur du flash avec la couleur de l'anneau lumineux" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronise la couleur du flash avec la couleur de l'anneau lumineux" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchronise la couleur du flash avec la couleur de l'anneau lumineux" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronise la couleur du flash avec la couleur de l'anneau lumineux" } } } }, - "Syncs settings across all your devices via iCloud": { - "comment": "An accessibility hint describing the functionality of the sync toggle in the settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Sincroniza la configuración en todos tus dispositivos vía iCloud" + "Syncs settings across all your devices via iCloud" : { + "comment" : "An accessibility hint describing the functionality of the sync toggle in the settings view.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sincroniza la configuración en todos tus dispositivos vía iCloud" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Synchronise les réglages sur tous vos appareils via iCloud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronise les réglages sur tous vos appareils via iCloud" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Synchronise les réglages sur tous vos appareils via iCloud" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synchronise les réglages sur tous vos appareils via iCloud" } } } }, - "Take photo": { - "comment": "An accessibility label for the capture button.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Tomar foto" + "Take photo" : { + "comment" : "An accessibility label for the capture button.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tomar foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Prendre une photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prendre une photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Prendre une photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prendre une photo" } } } }, - "Tap to collapse settings": { - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Toca para contraer configuración" + "Tap to collapse settings" : { + "extractionState" : "stale", + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toca para contraer configuración" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Appuyez pour réduire les réglages" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appuyez pour réduire les réglages" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Appuyez pour réduire les réglages" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appuyez pour réduire les réglages" } } } }, - "Tap to expand camera settings": { - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Toca para expandir configuración de cámara" + "Tap to expand camera settings" : { + "extractionState" : "stale", + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Toca para expandir configuración de cámara" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Appuyez pour développer les réglages de caméra" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appuyez pour développer les réglages de caméra" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Appuyez pour développer les réglages de caméra" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Appuyez pour développer les réglages de caméra" } } } }, - "Third-party libraries used in this app": { - "comment": "A description of the third-party libraries used in this app.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Bibliotecas de terceros utilizadas en esta app" + "Third-party libraries used in this app" : { + "comment" : "A description of the third-party libraries used in this app.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bibliotecas de terceros utilizadas en esta app" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Bibliothèques tierces utilisées dans cette app" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bibliothèques tierces utilisées dans cette app" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Bibliothèques tierces utilisées dans cette app" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bibliothèques tierces utilisées dans cette app" } } } }, - "True Mirror": { - "comment": "Title of a toggle in the settings view that allows the user to flip the camera preview.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Espejo Real" + "True Mirror" : { + "comment" : "Title of a toggle in the settings view that allows the user to flip the camera preview.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Espejo Real" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Miroir Réel" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Miroir Réel" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Miroir Réel" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Miroir Réel" } } } }, - "True Mirror Mode": { - "comment": "Feature of the Pro subscription that allows users to see their reflection in the mirror.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Modo Espejo Real" + "True Mirror Mode" : { + "comment" : "Feature of the Pro subscription that allows users to see their reflection in the mirror.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Modo Espejo Real" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Mode Miroir Réel" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode Miroir Réel" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Mode Miroir Réel" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mode Miroir Réel" } } } }, - "Upgrade to Pro": { - "comment": "A button label that prompts users to upgrade to the premium version of the app.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Mejorar a Pro" + "Upgrade to Pro" : { + "comment" : "A button label that prompts users to upgrade to the premium version of the app.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mejorar a Pro" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Passer à Pro" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passer à Pro" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Passer à Pro" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passer à Pro" } } } }, - "Upgrade to unlock 5s and 10s timers": { - "comment": "A message displayed to users who want to upgrade to unlock longer self-timer durations.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Mejora para desbloquear temporizadores de 5s y 10s" + "Upgrade to unlock 5s and 10s timers" : { + "comment" : "A message displayed to users who want to upgrade to unlock longer self-timer durations.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mejora para desbloquear temporizadores de 5s y 10s" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Passez à Pro pour débloquer les retardateurs de 5s et 10s" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passez à Pro pour débloquer les retardateurs de 5s et 10s" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Passez à Pro pour débloquer les retardateurs de 5s et 10s" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passez à Pro pour débloquer les retardateurs de 5s et 10s" } } } }, - "Upgrade to unlock High quality": { - "comment": "A message displayed to users who want to upgrade to access higher image quality for their saved photos.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Mejora para desbloquear Alta calidad" + "Upgrade to unlock High quality" : { + "comment" : "A message displayed to users who want to upgrade to access higher image quality for their saved photos.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mejora para desbloquear Alta calidad" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Passez à Pro pour débloquer la Haute qualité" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passez à Pro pour débloquer la Haute qualité" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Passez à Pro pour débloquer la Haute qualité" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Passez à Pro pour débloquer la Haute qualité" } } } }, - "Use ring light color for screen flash": { - "comment": "Accessibility hint for the \"Flash Sync\" toggle in the Settings view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Usar color del aro de luz para flash de pantalla" + "Use ring light color for screen flash" : { + "comment" : "Accessibility hint for the \"Flash Sync\" toggle in the Settings view.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usar color del aro de luz para flash de pantalla" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Utiliser la couleur de l'anneau lumineux pour le flash d'écran" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliser la couleur de l'anneau lumineux pour le flash d'écran" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Utiliser la couleur de l'anneau lumineux pour le flash d'écran" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utiliser la couleur de l'anneau lumineux pour le flash d'écran" } } } }, - "Use the buttons at the bottom to save or share your photo": { - "comment": "An accessibility hint for the photo review view, instructing the user on how to interact with the view.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Usa los botones de abajo para guardar o compartir tu foto" + "Use the buttons at the bottom to save or share your photo" : { + "comment" : "An accessibility hint for the photo review view, instructing the user on how to interact with the view.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usa los botones de abajo para guardar o compartir tu foto" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Utilisez les boutons en bas pour enregistrer ou partager votre photo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utilisez les boutons en bas pour enregistrer ou partager votre photo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Utilisez les boutons en bas pour enregistrer ou partager votre photo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utilisez les boutons en bas pour enregistrer ou partager votre photo" } } } }, - "Uses screen flash when taking front camera photos": { - "comment": "A toggle that enables or disables the use of the front camera's flash during photo captures.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Usa flash de pantalla al tomar fotos con cámara frontal" + "Uses screen flash when taking front camera photos" : { + "comment" : "A toggle that enables or disables the use of the front camera's flash during photo captures.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usa flash de pantalla al tomar fotos con cámara frontal" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Utilise le flash d'écran lors de la prise de photos avec la caméra frontale" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utilise le flash d'écran lors de la prise de photos avec la caméra frontale" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Utilise le flash d'écran lors de la prise de photos avec la caméra frontale" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utilise le flash d'écran lors de la prise de photos avec la caméra frontale" } } } }, - "Video": { - "comment": "Display name for the \"Video\" capture mode.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Video" + "Video" : { + "comment" : "Display name for the \"Video\" capture mode.", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Video" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Vidéo" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vidéo" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Vidéo" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vidéo" } } } }, - "View on GitHub": { - "comment": "A button label that says \"View on GitHub\".", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Ver en GitHub" + "View on GitHub" : { + "comment" : "A button label that says \"View on GitHub\".", + "extractionState" : "stale", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ver en GitHub" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Voir sur GitHub" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voir sur GitHub" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Voir sur GitHub" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voir sur GitHub" } } } }, - "Warm Amber": { - "comment": "Name of a ring light color preset.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Ámbar Cálido" + "Warm Amber" : { + "comment" : "Name of a ring light color preset.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ámbar Cálido" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Ambre Chaud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ambre Chaud" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Ambre Chaud" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ambre Chaud" } } } }, - "Warm Cream": { - "comment": "A color option for the ring light, named after a warm, creamy shade of white.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Crema Cálido" + "Warm Cream" : { + "comment" : "A color option for the ring light, named after a warm, creamy shade of white.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crema Cálido" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Crème Chaud" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crème Chaud" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Crème Chaud" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Crème Chaud" } } } }, - "When enabled, photos and videos are saved immediately after capture": { - "comment": "A hint provided by the \"Auto-Save\" toggle in the Settings view, explaining that photos and videos are saved immediately after capture when enabled.", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Cuando está activado, las fotos y videos se guardan inmediatamente después de capturar" + "When enabled, photos and videos are saved immediately after capture" : { + "comment" : "A hint provided by the \"Auto-Save\" toggle in the Settings view, explaining that photos and videos are saved immediately after capture when enabled.", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cuando está activado, las fotos y videos se guardan inmediatamente después de capturar" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Lorsqu'activé, les photos et vidéos sont enregistrées immédiatement après la capture" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lorsqu'activé, les photos et vidéos sont enregistrées immédiatement après la capture" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Lorsqu'activé, les photos et vidéos sont enregistrées immédiatement après la capture" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lorsqu'activé, les photos et vidéos sont enregistrées immédiatement après la capture" } } } }, - "Zoom %@ times": { - "comment": "A label describing the zoom level of the camera view. The argument is the string \"%.1f\".", - "isCommentAutoGenerated": true, - "localizations": { - "es-MX": { - "stringUnit": { - "state": "translated", - "value": "Zoom %@ veces" + "Zoom %@ times" : { + "comment" : "A label describing the zoom level of the camera view. The argument is the string \"%.1f\".", + "isCommentAutoGenerated" : true, + "localizations" : { + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoom %@ veces" } }, - "fr": { - "stringUnit": { - "state": "translated", - "value": "Zoom %@ fois" + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoom %@ fois" } }, - "fr-CA": { - "stringUnit": { - "state": "translated", - "value": "Zoom %@ fois" + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zoom %@ fois" } } } } }, - "version": "1.1" -} + "version" : "1.1" +} \ No newline at end of file